Activity

前言

  • 参考书:Android 开发艺术探索

  • 本篇主要介绍 Activity 的生命周期、启动模式和 Intentfliter 的匹配规则

正文

一、Activity 的生命周期

分两种: 正常情况下和异常情况下的生命周期

1.1 正常情况下的生命周期,从上到下
  • onCreate(): 表示 Activity 正在创建,可以做一些初始化工作。

  • onStart(): 表示 Activity 正在启动,这时 Acticity 已经创建出来了,只是没有出现在前台,用户看不到

  • onResume(): 表示 Activity 已经可见,可以与用户进行交互

  • onPause(): 表示 Acticity 正在停止,此时可以做一些停止动画、存储数据等操作,但是注意不能耗时,因为只有这个方法执行完毕,新的 Activity 的 onResume 才会执行

  • onStop(): 表示 Activity 即将停止,可以做一些重量级的回收工作,但是不能耗时

  • onDestroy(): 表示 Activity 即将被销毁,可以做一些回收工作和资源释放

还有一个 onRestart() 方法,此方法表示 Activity 正在重新启动,一般情况下在 Activity 从不可见到可见状态的时候,会调用此方法,比如说 A 跳转到 B 然后再返回 A

1.2 异常情况下的生命周期
1.2.1 资源相关的系统配置发生改变导致 Activity 被杀死并重新创建

比如说,横竖屏切换的时候,显示的图片资源及布局文件等系统配置发生改变,默认情况下,Activity 会被销毁并重新创建,其生命周期方法如下:

  • 首先会调用 onSaveInstanceState() 方法,做数据保存工作,系统默认为我们保存当前 Activity 的视图结构,并在 Activity 重启后,为我们恢复这些数据,比如 文本框中用户输入的数据,ListView 滚动的位置等,但是如果针对于某个 View 来说,能恢复什么数据,可以看 View 的源码来分析,因为 View 和 Activity 一样,都有 onSaveInstanceState() 方法来保存数据,保存的数据在其参数 Bundle 中

  • 其次,调用 onPause() 、onStop()、onDestory() 方法,注意 onSaveInstanceState() 方法可能在 onPause() 之前调用,也可能在其之后调用。

  • 然后,调用 onCreat() 方法,然后继续调用 onRestoreInstanceState() 方法来为其恢复状态和数据,我们可以根据这两个方法中的参数 Bundle 来判断此 Activity 是否被重建

1.2.2 资源内存不足,导致低优先级的 Activity 被杀死

这种方式与上边的第一种情况保存和恢复数据的方法一致,这里说下 Activity 的优先级,从高到低,如下所示:

  • 前台 Activity ,正在与用户交互,优先级最高

  • 可见,但并非前台 Activity ,比如 Activity 中弹出一个对话框,导致 Activity 可见,但是无法与用户交互

  • 后台 Activity ,已经被暂停的 Activity ,比如执行了 onStop() 方法,优先级最低

另外注意,如果一个进程中没有四大组件运行,那么这个进程将会很快被杀死!

1.2.3 阻止 Activity 重新创建

在 AndroidMenifest.xml 中为 Activity 添加一条 configChanges 属性 :

1
android:configChanges="orientation"

常用属性值的含义:

  • locale :设备的本地位置发生了改变,一般指切换了系统语言

  • orientation :设备屏幕发生改变,比如旋转屏幕

  • keyboardHidden :键盘的可访问性发生了改变,比如用户调出了键盘

  • screenSize :屏幕尺寸信息发生改变,常用于旋转屏幕导致的 尺寸信息改变,一般和 orientation 配合使用

  • smallestScreenSize :表示设备的物理屏幕尺寸发生改变,注意指的是物理设备,跟旋转屏幕没关系,比如用户切换到了外部的显示设备,这个不常用。

二、Activity 的启动模式

2.1 四种启动模式
  • standard :标准模式,也是系统的默认模式,每次启动一个 Activity 都会创建一个新实例,不管这个实例是否存在,会一一放入任务栈中,然后按回退键的时候,会按照先进后出的原则,一一退栈!

  • singleTop :栈顶复用模式,如果新创建的 Activity 已经位于任务栈的栈顶了,那么此 Activity 不会被重建,注意这个 Activity 的 onCreat()、onstart() 方法不会被调用,因为它并没有发生改变,但是会回调 onNewIntent(Intent intent) 方法,从而获取 intent 信息。

  • singleTask :栈内复用模式,这是一种单实例模式,只要 Activity 在一个栈中存在,那么多次启动 Activity 都不会重新创建实例,和 singleTop 一样,也会回调其 onNewIntent(Intent intent) 方法,但是需要注意一点,如果栈中有的话,该模式,会把所需要的 Activity 之上的所有 Activity 弹出栈,将需要的这个 Activity 放于栈顶!

  • singleInstance : 它是一种加强版的 singleTask ,它除了具有 singleTask 的所有特性之外,还加强了一点,就是只能单独的位于一个栈中!一个栈只能有一个 Activity

2.2 为 Activity 指定启动模式
  • 第一种,通过 AndroidMenifest.xml 文件中,为其添加属性
1
android:launchMode="singleTop"
  • 第二种,通过在 Intent 中设置标志位 Flags 来为 Activity 指定模式
1
2
3
Intent intent = new Intent(this,TextActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
  • 两种方式的区别:
  1. 优先级,第二种要高于第一种,两种都存在的话,以第二种为准;
  2. 限定范围不同,第一种无法为 Activity 指定 FLAG_ACTIVITY_CLEAR_TOP 标识,第二种无法为 Activity 指定 singleInstance 模式!
2.3 Activity 的 Flags

Activity 的 Flags 有很多,作用也很多,有些可以设定 Activity 的启动模式,有些可以影响 Acticity 的运行状态,这里记录些比较常用的 Flags

  • FLAG_ACTIVITY_NEW_TASK ,指定 Activity 的启动模式为 singleTask

  • FLAG_ACTIVITY_SINGLE_TOP ,指定 Activity 的启动模式为 singleTop

  • FLAG_ACTIVITY_CLEAR_TOP ,当 Activity 启动时,与其在同一个任务栈并在其上边的所有 Activity 全部出栈,一般和 singleTask 启动模式一块出现

  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS ,在某些情况下,我们不希望用户通过历史列表回到我们的 Activity 的时候,使用这个标记,等同于 xml 文件中的属性: android:excludeFromRecents=”true”

三、Activity 中 IntentFilter 匹配规则

  • Activity 的启动方式有两种,显式启动和隐式启动,如果两者都存在那么以显示为主,显式启动很简单,主要记录下隐式启动

  • 隐式启动需要 Intent 能够匹配目标组件的 IntentFilter 中的过滤信息,过滤信息有三个,分别是 action 、 category 、 data,下边针对这三个过滤信息做介绍

3.1 action 的匹配规则
  • action 只是一个字符串,区分大小写,系统中有预定义的一些 action 我们也可以自定义自己所需要的 action, action 可以有多个,但是我们只要匹配成功一个即可匹配成功,需要注意的是,只要是隐式启动,那么 Intent 中必须要有 action
3.2 category 的匹配规则
  • category 也是一个字符串,category 也可以有多个,但是必须要每个都匹配成功才行,另外,如果需要隐式启动,那么必须添加 android.intent.category.DEFAULT 这个 category ,因为我们调用 startActivity 方法的时候,系统会默认为我们添加这个一个 category ,所以必须要添加
3.3 data 的匹配规则
  • data 相对复杂一点,结构如下
1
2
3
4
5
6
7
<data android:scheme="aaa"
android:host="aaa"
android:port="aaa"
android:path="aaa"
android:pathPattern="aaa"
android:pathPrefix="aaa"
android:mimeType="aaa"/>

主要是有两部分组成 mimeType 和 Uri

  1. mimeType 指媒体类型 例如: image/jpeg vided/* …
  2. Uri 的结构如下:
1
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

Uri 的属性介绍如下:

1
2
3
4
scheme:整个URI的模式,如常见的http,file等,注意如果URI中没有指定的scheme,那么整个uri无效
host:URI的域名,比如我们常见的www.mi.com,www.baidu.com,与scheme一样,一旦没有host那么整个URI也毫无意义;
port:端口号,比如80,很容易理解,只有在URI中指定了scheme和host之后端口号才是有意义的;
path,pathPattern,pathPrefix包含路径信息,path表示完整的路径,pathPattern在此基础上可以包含通配符,pathPrefix表示路径的前缀信息;
  • 只匹配 scheme 的例子如下:
1
2
3
4
5
6
7
8
9
<activity android:name=".TextActivity">
<intent-filter>
<action android:name="111"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<data android:scheme="222"></data>
</intent-filter>
</activity>
1
2
3
4
5
6
Intent intent = new Intent();
intent.setAction("111");
// scheme:// 固定写法,后边的 111 是自己随便写的
intent.setData(Uri.parse("222://111"));
startActivity(intent);
  • 只匹配 mimeType 的例子如下
1
2
3
4
5
6
7
8
9
<activity android:name=".TextActivity">
<intent-filter>
<action android:name="111"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<data android:mimeType="image/*"></data>
</intent-filter>
</activity>
1
2
3
4
5
6
Intent intent = new Intent();
intent.setAction("111");
// 这种情况,scheme 默认为 content ,亲测 file 会报错
//注意此处是 setDataAndType
intent.setDataAndType(Uri.parse("content://111"),"image/png");
startActivity(intent);
  • 注意,data,不是必须要添加的,也就是说如果只有 action 和 category 也能匹配成功
  • 还有一点,如果我们要启动系统的页面,如打电话页面,需要找到其匹配规则,进行匹配,然后启动

  • 另外,IntentFilter 可以有多对,只要匹配其中一对即可成功。