一 四大组件
1.1 Activity组件,它一个单独的窗口,程序流程都必须在Activity中运行。
1.2 service组件,用于在后台完成用户指定的操作。
1.3 content provider组件,会为所有的应用准备一个内容窗口,并且保留数据库、文件。
1.4 broadcast receiver组件,是程序之间传递信息时的一种机制,作用就是接收或者发送通知。
二 Activity简介
2.1 Activity是四大组件中用的最多的,是数据显示的载体,也是用户视觉显示和行为操控的界面。
2.2 一个程序包含一个或者多个Activity,Activity之间的跳转和数据通信依赖于Intent(意图)。
三 Activity的创建
3.1 Actvity是一个Java类,先创建一个Class,继承于Activity或者
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
3.2 创建xml布局文件
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
...................
</FrameLayout>
3.3 在清单文件AndroidManifest.xml注册Activity组件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3.5 Intent启动组件
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
四 Context上下文
4.1 很多人刚接触Context不太理解是干什么用的,上下文又是什么意思。Context其实是一个程序运行的环境,在这个环境里面可以做四大组件的启动,页面的跳转,资源的访问。总结起来就是应用程序和系统之间的桥梁,应用程序访问系统各种资源的接口
4.2 Context种类也不止一种,虽然都可以去访问系统资源,但各自的生命周期不一样。可以细分为以下三类:Application,Activity,service
4.3 生命周期的对比
Application的生命周期是最长的,一般应用不杀死就会一直存在。
Activity和Service的生命周期比较短,所以内存泄漏大部分发生在这两种Context的引用上面
4.4 Context使用场景对比
五 Activity生命周期
5.1 生命周期图示
5.2 如下复写Activity的生命周期
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//当Activity第一次被创建的时候调用此方法.一般在此方法中进行控件的声明,添加事件等初始化工作.
}
@Override
protected void onStart() {
super.onStart();
//当Activity被显示到屏幕上的时候调用此方法.
}
@Override
protected void onResume() {
super.onResume();
//当此Activity能够被操作之前,也就是能够获得用户的焦点之前调用此方法.
}
@Override
protected void onRestart() {
super.onRestart();
//当Activity被停止后又被再次启动之前调用此方法.接着将调用onStart()方法.
}
@Override
protected void onPause() {
super.onPause();
//当第一个Activity通过Intent启动第二个Activity的时候,将调用第一个Activity的onPause()方法.然后调用第二个Activity的onCreate(),onStart(),onResume()方法,接着调用第一个Activity的onStop()方法.如果Activity重新获得焦点,则将调用onResume()方法;如果此Activity进入用户不可见状态,那么将调用onStop()方法
}
@Override
protected void onStop() {
super.onStop();
//当第一个Activity被第二个Activity完全覆盖,或者被销毁的时候回调用此方法.如果此Activity还会与用户进行交互,将调用onRestart方法();如果此Activity将被销毁,那么将调用onDestroy()方法.
}
@Override
protected void onDestroy() {
super.onDestroy();
//Activity被销毁之前调用此方法.或者是调用finish()方法结束Activity的时候调用此方法.可以在此方法中进行收尾工作,比如释放资源等.
}
}
5.3 比如两个Activity,AActivity.java和BActivity.java
打开AActivity,生命周期如下:
AActivity onCreate() -> AActivity onStart() -> AActivity onStart()
从AActivity跳转BActivity,生命周期如下:
AActivity onPause() -> BActivity oncreate() -> BActivity onstart() -> BActivity onresume() -> AActivity onstop()
从BActivity返回AActivity,生命周期如下:
BActivity onPause() -> AActivity onRestart() -> AActivity onStart() -> AActivity onResume() -> BActivity onStop() -> BActivity onDestroy()
5.4 Activity启动模式
- standard:默认启动模式,每次激活activity时,都创建activity实例,并放入任务栈。
- singleTop: 如果某个Activity自己激活自己,即任务栈栈顶是该Activity,则不需要创建,其余情况都要创建Activity。
- singleTask:如果要激活的activity在任务栈中,则不需要创建,只需要把这个Activity放入栈顶,并把该Activity以上的Activity实例都出栈。
- singleInstance:只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。
设置启动模式两种方式:
静态设置:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity
android:name=".MainActivity"
android:launchMode="standard"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Activity2"
android:launchMode="singleTop"/>
<activity android:name=".Activity3"
android:launchMode="singleTask"/>
<activity android:name=".Activity4"
android:launchMode="singleInstance"/>
</application>
动态设置:
//默认不设置,即standard
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
//FLAG_ACTIVITY_SINGLE_TOP,即singleTop
Intent intent2 = new Intent(this,Activity2.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent2);
//FLAG_ACTIVITY_CLEAR_TOP,即singleTask
Intent intent3 = new Intent(this,Activity3.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent3);
//FLAG_ACTIVITY_NEW_TASK,即singleInstance
Intent intent4 = new Intent(this,Activity4.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent4);
六 Intent意图
6.1 前面说过Intent也非常重要,是连接组件的桥梁,用来启动activity, service和broadcast receiver和组件之间的通信,而Content Provider本身就是一种通信机制,不需要通过Intent。
6.2 Intent启动组件
- 使用Context.startActivity() 或 Activity.startActivityForResult(),传入一个intent来启动一个activity。使用 Activity.setResult(),传入一个intent来从activity中返回结果。
- 将intent对象传给Context.startService()来启动一个service或者传消息给一个运行的service。将intent对象传给 Context.bindService()来绑定一个service。
- 将intent对象传给 Context.sendBroadcast(),Context.sendOrderedBroadcast(),或者Context.sendStickyBroadcast()等广播方法,则它们被传给 broadcast receiver
6.3 Intent的属性
- component(组件):目的组件,一般是类名完整路径(包名.类名)
- action(动作):用来表现意图的行动
- category(类别):用来表现动作的类别
- data(数据):表示与动作要操纵的数据
- type(数据类型):对于data范例的描写
- extras(扩展信息):扩展信息
- Flags(标志位):期望这个意图的运行模式
根据不同的属性设置,启动Activity分为显式和隐式:
- 显式即直接启动,明确指向一个Activity类组件。
- 隐式指间接启动,根据action,category,data等筛选查找到匹配的组件。
6.4 显式启动的三种方式:
方式一:直接启动
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
方式二:setComponent新建组件启动
Intent intent = new Intent();
intent.setComponent(new ComponentName(this, MainActivity.class);)
startActivity(intent);
方式三:setClassName设置全Activity全路径
Intent intent = new Intent();
intent.setClassName(this,"com.dinghe.schemetestmain.MainActivity");
startActivity(intent);
6.5 隐式启动设置Action:
方式一:设置Action
如调用系统程序,系统Action
Intent intent = new Intent();
拨打电话
intent.setAction(Intent.ACTION_CALL);
发送短信
intent.setAction(Intent.ACTION_SENDTO);
打开相机
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
//启动
startActivity(intent);
方式二:如自定义Action,在AndroidManifest.xml里面设置intent-filter的Action
<activity
android:name=".ActionActivity"
android:exported="true">
<intent-filter>
<action android:name="com.dinghe.schemetest.customerAction"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction(getPackageName()+".customerAction");
startActivity(intent);
方式三:设置action+data,打开网站:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
方式四:设置action+data+type,打开指定类型文件
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri data = Uri.parse("file:///storage/sdcard0/test.mp3");
//设置data+type属性
intent.setDataAndType(data, "audio/mp3"); //方法:Intent android.content.Intent.setDataAndType(Uri data, String type)
startActivity(intent);
方式五:设置action+category+data+extra+flags
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SENDTO);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse("smsto:"+"10086"));
intent.putExtra("keyName","keyValue");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
注意: 如果自己定义的某个Action要通过隐式启动,在AndroidManifast.xml中必须加上 android.intent.category.DEFAULT,否则不起作用
七 Activity间通信
7.1 Activity传递数据要用到Intent.putExtra()方法,里面可以传递任意类型的数据,如下图所示传递基本类型,数组类型,字节类类型
集合类型,和Bundle类型
7.2 Intent传递数据
Intent intent = new Intent(this,MainActivity.class);
intent.putExtra("intKey",1);
intent.putExtra("StringKey","111");
startActivity(intent);
7.3 Bundle传递数据
Intent intent = new Intent(this,MainActivity.class);
Bundle bundle=new Bundle();
bundle.putInt("intKey",1);
bundle.putString("StringKey","111");
intent.putExtras(bundle);
startActivity(intent);
八 Activity启动流程
1. Launcher进程请求AMS
2. AMS发送创建应用进程请求
3. Zygote进程接受请求并孵化应用进程
4. 应用进程启动ActivityThread
5. 应用进程绑定到AMS
6. AMS发送启动Activity的请求
7. ActivityThread的Handler处理启动Activity的请求
九 Activity管理和回退
9.1 监听程序的后台和前台切换,如返回桌面和从桌面打开的状态。
利用Application.ActivityLifecycleCallbacks的方法,全局监听Activity的打开数量,数量0表示在后台,数量1表示前台
如下前后台监听的具体实现:
private Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
//打开的Activity数量统计
private int activityStartCount = 0;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
activityStartCount++;
//数值从0变到1说明是从后台切到前台
if (activityStartCount == 1) {
//从后台切到前台
}
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
activityStartCount--;
//数值从1到0说明是从前台切到后台
if (activityStartCount == 0) {
//从前台切到后台
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
};
9.2 定义Activity回退栈,在Activity的onCreate里面添加进栈,可以在任意页面来结束指定页面或者全部页面
如下新建Activity栈的管理工具类:
public class ActivityUtil {
public static Stack<Activity> activityStack = new Stack<Activity>();
/**
* 添加Activity到堆栈
*/
public static void addActivity(Activity activity) {
activityStack.push(activity);
}
public static void removeActivity(Activity activity) {
activityStack.remove(activity);
}
/**
* 获取当前Activity(堆栈中最后一个压入的)
*/
public static Activity currentActivity() {
return activityStack.lastElement();
}
/**
* 结束当前Activity(堆栈中最后一个压入的)
*/
public static void finishCurrentActivity() {
Activity activity = activityStack.pop();
activity.finish();
}
/**
* 结束指定的Activity
*/
public static void finishActivity(Activity activity) {
if (activity != null) {
// activityStack.remove(activity);
if (!activity.isFinishing()) {
activity.finish();
}
}
}
/**
* 结束指定的Activity
*/
public static void finishActivity(Activity activity,boolean isRemoveStack) {
if (activity != null) {
if (!activity.isFinishing()) {
activity.finish();
if(isRemoveStack){
activityStack.remove(activity);
}
}
}
}
public static void finishExcept(Class<?> cls) {
for (Activity activity : activityStack) {
if (!activity.getClass().equals(cls)) {
finishActivity(activity);
}
}
}
/**
* 结束指定类名的Activity
*/
public static void finishActivity(Class<?> cls) {
for (Activity activity : activityStack) {
if (activity.getClass().equals(cls)) {
finishActivity(activity);
}
}
}
/**
* 结束所有Activity
*/
public static void finishAllActivity() {
for (Activity activity : activityStack) {
if (activity != null) {
activity.finish();
}
}
activityStack.clear();
}
/**
* 退出应用程序
*/
@SuppressLint("MissingPermission")
public static void AppExit(Context context) {
try {
finishAllActivity();
ActivityManager manager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
manager.killBackgroundProcesses(context.getPackageName());
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
添加Activity到栈
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//添加Activity到栈
ActivityUtil.addActivity(this);
}
}
结束指定Activity
ActivityUtil.finishActivity(MainActivity.class);
十 两个app的通信和跳转
10.1 我们平时打开京东或者其它app,可以从应用内打开另一个应用,或者微信分享可以直接打微信的程序,还有网页H5可以跳转指定其它app页面,那这是怎样实现的呢。这就用到了应用间的跨进程跳转。
10.2 两个app,从一个app打开另一个app实现,想要打开另一个app,必须先知道app的包名,再根据包名显式和隐式的打开指定Activity。
10.3 显式方式跳转,跟正常Intent显式启动一样,指定包名和类型就可以打开
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName componentName = new ComponentName("com.dinghe.schemetest", "com.dinghe.schemetest.MainActivity");
intent.setComponent(componentName);
startActivity(intent);
10.4 网页H5跳转另一个app,通过intent-filter的data里面scheme实现。
scheme是一种页面内跳转协议,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;通过scheme协议,服务器可以定制化告诉App跳转到APP内部页面
实现步骤:
第一步:新建一个html网页test.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<!-- <a href="[scheme]://[host]/[path]?[query]">启动应用程序</a> -->
<!-- scheme:判别启动的App -->
<!-- host:主机 没有也可以 -->
<!-- path:传值时必须的key 没有也可以 -->
<!-- query:获取值的Key和Value 没有也可以 -->
<a href="goods://test:8080/details?id=222">启动应用</a>
<br/>
</body>
</html>
第二步:新建目标协议页面SchemeActivity.java
public class SchemeActivity extends AppCompatActivity {
private TextView tvContent;
String id;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scheme);
id=getIntent().getStringExtra("id");
tvContent = (TextView) findViewById(R.id.tv_content);
Intent intent = getIntent();
Uri data = intent.getData(); //
String action = intent.getAction();
String scheme = intent.getScheme();
Set<String> categories = intent.getCategories();
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("data:").append(data).append("\n\n")
.append("action:").append(action).append("\n\n")
.append("categories:").append(categories).append("\n\n")
.append("scheme:").append(scheme).append("\n\n")
.append("id:").append(data.getQueryParameterNames()).append("\n\n")
.append("host:").append(data.getHost()).append("\n\n")
.append("path:").append(data.getPath()).append("\n\n")
.append("port:").append(data.getPort()).append("\n\n")
;
tvContent.setText(stringBuilder);
}
}
第三步:AndroidManifest.xml里面注册SchemeActivity的协议
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity
android:name=".SchemeActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="goods"
android:host="test"
android:port="8080"
android:path="/details"
/>
</intent-filter>
</activity>
</application>
第四步:跳转该协议,点击h5网页里面的启动应用按钮,就能跳转另一个app了
10.6 利用三方SDK实现网页跳转其它app的方案,比如极光魔链平台运用这种短链可以实现该方案
如下流程
创建短链