segmentfault 对 mackdown 语法的支持不是很好,有些图片都显示不出来,大家可以去我的掘金查看这篇文章。
一、概述
简单来讲,Activity 就是一个可视化界面,负责承建一个屏幕窗口,防止 UI 组件,供用户交互。一般来说承建 Activity 有三个步骤:
- 承建 Activity 类;
- 在 AndroidManifest.xml 中注册;
- 设置布局文件(可选)。
二、Activity 的启动方法
2.1、显示启动
明确指定要启动的 Activity 的 $\color{red}{class}$ 或者 $\color{red}{包名.activity类名}$,显示启动主要有三种方式:
方式一:class 跳转(最常用)
Intent intent = new Intent(MainActivity.this, SecondActivity.class); startActivity(intent);
方式二:包名.类名 跳转
Intent intent = new Intent(); intent.setClassName(MainActivity.this, "com.zjgsu.activitydemo.SecondActivity"); startActivity(intent);
方式三:ComponentName 跳转
Intent intent = new Intent(); intent.setComponent(new ComponentName(MainActivity.this, SecondActivity.class)); startActivity(intent);
2.2、隐式启动
设置启动过滤器,通过指定的 $\color{red}{action}$ 或 $\color{red}{action 和 data}$ 属性,系统会查找符合条件的 Activity,并启动它,隐私启动主要有两种方式:
方式一:传入 actionName
Intent intent = new Intent("abcd.SecondActivity"); startActivity(intent);
方式二:设置 action
Intent intent = new Intent(); intent.setAction("abcd.SecondActivity"); startActivity(intent);
注意:如果自己定义的某个 Activity 要通过隐式启动,在 AndroidManifest.xml 中必须加上 android.intent.category.DEFAULT,否则不起作用。
2.2.1、隐式启动如果两个 activity 的 actionName是一样的会怎样呢?
如下所示,我创建了两个 Activity,然后这两个 Activity 的 actionName 我设置成一样的,然后我通过隐式启动 Activity,会怎样呢?
运行效果如下所示:
看上面的 gif 动图我们就可以看出来,这种情况下 Android 会让我们选择要运行的 Activity。即当有多个 Action 匹配隐式匹配的条件时,将会弹出选择框供用户选择。
三、Activity 的生命周期
我们就以下面这张 Activity 生命周期图为例子吧:
一个 Activity 启动的时候会依次调用 onCreate() --> onStart() --> onResume() --> onPause() --> onStop() --> onDestroy()。当执行到 onResume() 的时候这个 Activity 就变成了可操作状态。
3.1、单 Activity 生命周期的调用顺序
- onCreate() 创建 Activity 时调用
- onStart() 当 Activity 界面变为用户可见时调用
- onResume() 当 Activity 界面获取到焦点时调用(界面按钮可点击,文本框可输入等)
- onPause() 当 Activity 界面失去焦点时调用(界面按钮不可点击,文本框不可输入等)
- onStop() 当 Activity 界面变为用户不可见时调用
- onDestroy() 当 Activity 被销毁时调用
- onRestart() 当 Activity 再次启动时调用
3.2、多 Activity 生命周期的调用顺序
四、Activity 的启动模式
Activity 的启动模式决定了新生产的 Activity 实例是否重用已存在的 Activity 实例,是否和其他 Activity 实例共用一个 Task。Task 是一个具有栈结构的对象,一个 Task 可以管理多个 Activity,启动一个应用,也就创建一个与之对应的 Task。
4.1、四种启动模式
- standard
默认的启动模式,每次激活 Activity 时(startActivity),都创建 Activity 实例,并放入任务栈; - singleTop
每次激活 Activity 时,判断该 Activity 实例是不是在栈顶,如果是,不需要创建,否则需要创建 Activity 实例; - singleTask
如果要激活的那个 Activity 在任务栈中已经存在了,则不需要创建,只需要把此 Activity 以上的 Activity 实例都出栈,这个时候此 Activity 就到栈顶了,若不存在,就创建 Activity 实例; - singleInstance
只有一个实例,并且这个实例独立运行在一个 Task 中,这个 Task 只有这个实例,不允许有别的 Activity 实例存在。
4.2、四种启动模式代码演示
4.2.1、standard
我们先来看下效果:
可以看到,在 SecondActivity 里面再次启动 SecondActivity,standard 模式下的启动方式会再次创建 SecondActivity 实例。使得任务栈中包含了两个 SecondActivity 实例,所以我们点击返回键只是返回了上一个 SecondActivity,再次按返回键才回到 MainActivity。
主要代码如下所示:
// 启动模式是 standard,不填默认就是 standard
// SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.btn_start_self).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SecondActivity.this, SecondActivity.class));
}
});
}
}
4.2.2、singleTop
我们还是用上面的例子来演示,只是把 SecondActivity 的启动模式改成 singleTop,我们来看下效果:
可以看到,这个时候在 SecondActivity 里面再次启动自己,是不会创建 SecondActivity 实例的,应该这个时候 SecondActivity已经在栈顶了,而 singleTop 启动模式会判断该 Activity 实例是不是在栈顶,如果是,不需要创建,否则才创建 Activity 实例。
主要代码如下所示:
问题1:如果这时候我们再创建一个 ThirdActivity,在 MainActivity 启动 SecondActivity,再在 SecondActivity 里面启动 ThirdActivity,然后在 ThirdActivity 里面启动 SecondActivity,这时候任务栈中 Activity 的实例顺序是怎样的?
答:任务栈中自底向上的 Activity 实例顺序为 MainActivity-->SecondActivity-->ThirdActivity-->SecondActivity。
4.2.3、singleTask
我们用上面的问题的例子来演示,只是把 SecondActivity 的启动模式改成 singleTask,我们来看下效果:
这个时候任务栈中自底向上的 Activity 实例顺序为 MainActivity–>SecondActivity。因为 SingleTask 启动模式如果栈中存在 Activity 实例会把该 Activity 以上的所有 Activity 实例全部出栈。
主要代码如下所示:
4.2.4、singleInstance
singleInstance 启动模式和 singleTask 启动模式有点像,都会保证任务栈中只有同一个 Activity 的实例,但是 singleInstance 会创建一个新的 Task。要想演示 singleInstance,我们需要把每个 Activity 所在的栈的 ID 打印出来。例子同上,只是修改启动模式为 singleInstance。
打印日志如下所示:
我们看到 SecondActivity 所在任务栈的 TaskId 是592,而 MainActivity 和 ThirdActivity 所在任务栈的 TaskId 是 591。这就说明 SecondActivity 运行在一个独立的任务栈里面,这个任务栈里面只有 SecondActivity 这一个 Activity 实例。打印 TaskId 的方法如下:
Log.e("activityDemo2TAG", "ThirdActivity所在的task的id为:" + getTaskId());
五、利用 IntentFlag 设置 Activity 的启动方式
讲解这个知识点之前我们先来看一下 Task 和 taskAffinity 这两个概念、
5.1、Task 基本概念
- Task 是一个具有栈结构的容器,可以放置多个 Activity 实例;
- 启动一个应用,系统会为之创建一个 Task,来放置根 Activity;
- 一个 Activity 启动另一个 Activity 时,默认情况下两个 Activity 是放置在同一个 Task 中的,后者被压入前者所在的 Task 栈,当用户按下返回键,后者从 Task 中被弹出,前者又显示在栈顶。
5.2、taskAffinity 基本概念
- 定义了 Activity 实例想要进入的 Task;
- 如果一个 Activity 没有显示的指明该 Activity 的 taskAffinity 属性,那么它的这个属性就等于 Application 所指明的 taskAffinity,如果 Application 也没有指明,那么该 taskAffinity 的值就等于包名。
5.3、IntentFlag 的常用值
IntentFlag 的种类很多,我们这里就只挑选几个平时常用的来讲解一下。
5.3.1、FLAG_ACTIVITY_NEW_TASK
系统会寻找或创建一个新的 Task 来放置目标 Activity,寻找时依据目标 Activity 的 taskAffinity 属性进行匹配,如果找到一个 Task 的 taskAffinity 与之相同,就将目标压入此 Task 中,如果查找无果,则创建一个新的 Task,并将该 Task 的 taskAffinity 值设置为目标 Activity 的 taskAffinity,将目标 Activity 放置于此 Task 中 。
5.3.1.1、代码演示
设置 IntentFlag 的方法:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
$\color{red}{第一种情况}$:以 FLAG_ACTIVITY_NEW_TASK 方式启动 SecondActivity,但是 SecondActivity 不添加 taskAffinity 属性,我们来看下效果:
可以看到,我们虽然以 FLAG_ACTIVITY_NEW_TASK 启动,但是两个 Activity 所在的 Task 还是同一个,这是为什么呢?那是因为如果一个 Activity 没有显示的指明该 Activity 的 taskAffinity 属性,那么它的这个属性就等于 Application 所指明的 taskAffinity,如果 Application 也没有指明,那么该 taskAffinity 的值就等于包名。 也就是说此时 SecondActivity 和 MainActivity 两个 taskAffinity 的值都是包名,所以 SecondActivity 肯定会被放入 MainActivity 所在的栈中了。
$\color{red}{第二种情况}$:以 FLAG_ACTIVITY_NEW_TASK 方式启动 SecondActivity,并且 SecondActivity 设置 taskAffinity 属性:
我们来看下效果:
可以看到这时候 SecondActivity 和 MainActivity 已经不在同一个栈里面了。
5.3.2、FLAG_ACTIVITY_SINGLE_TOP
同四种启动模式中的 singleTop,这里就不演示了。
5.3.3、FLAG_ACTIVITY_CLEAR_TOP
同四种启动模式中的 singleTask,这里也不演示。
5.3.4、FLAG_ACTIVITY_REORDER_TO_FRONT
这个启动模式的意思是如果栈中已经存在 Activity 实例,会将它拿到栈顶,不会启动新 Activity,也不会删除它之上的 Activity 实例。
5.3.4.1、代码演示
我们先来看下效果:
我们在 MainActivity 里面启动 SecondActivity,然后在 SecondActivity 里面启动 ThirdActivity,然后再在 ThirdActivity 里面通过 FLAG_ACTIVITY_REORDER_TO_FRONT 的方式启动 SecondActivity,这个时候因为栈中已经有 SecondActivity 实例了,所以会把该实例拿到栈顶,并且不会销毁 SecondActivity 之上的所有实例,所以此时栈自底向上的 Activity 实例顺序是 MainActivity --> ThirdActivity --> SecondActivity。
六、小结
这篇文章主要讲了 Activity 的五种启动方法,包括三种显示启动和两种隐式启动,又讲了单和多 Activity 的生命周期,以及在 AndroidManifest 中通过 lunchMode 设置 Activity 的启动模式和通过Intent.setFlag() 方法设置 Activity 的启动模式。
下一篇文章我们将会讲解在 Activity 中利用 Intent 传参、利用Bundle传递数据、复杂数据的传递以及启动系统 Activity的方法。