Android 生命周期与启动模式
1.1 Android 生命周期
参考资料:
https://www.gitbook.com/book/tom510230/android_ka_fa_yi_shu_tan_suo/details
官方文档
生命周期分为两部分:一部分是典型(正常)的生命周期,用户参与下所经历的生命周期,比如手动启动Activity和销毁Activity。另一部分是异常情况下的生命周期,比如Activity被系统回收或设备Configuration发生变化导致的Activity被销毁重建。
1.1.1 典型情况的生命周期
说明:
- 第一次启动某一个Activity回调:onCreate->onStart->onResume;
- back键退出当前Activity回调:onPause->onStop->onDestory;
- home键或打开新的Activity回调:onPause->onStop,如果Activity采用透明主题,则不会调用onStop。
- 再次回到原Activity回调:onRestart->onStart->onResume;
打开新的Activity回调:A:onPause-> B:onCreate-> B:onStart-> B:onResume-> A:onStop
说明:旧的Activity的onPause先调用,然后新的Activity才启动,所以在onPause中要避免做重量级操作,在官方文档中有体现:
1.1.2 异常情况下的生命周期分析
1. 资源相关的系统配置发生改变导致Activity被杀死
旋转屏幕等会导致系统配置发生变化,默认情况下,Activity会被销毁并且重新创建,也可以人为的阻止系统重建。
在默认情况下,不对Activity做任何处理,那么系统变化后,会被销毁和重建:生命周期如下:
只有在异常情况下(在home或启动其他Activity的时候也会单独调用onSaveInstanceState),Activity会在onStop之前调用onSaveInstanceState,与onPause无特定顺序,当Activity被重新创建的时候,系统会在onStart之后调用onRestoreInstanceState,并把Activity销毁时保存的bundle数据作为参数传给onRestoreInstanceState。
关于数据保存和恢复,Activity调用onSaveInstanceState保存数据--> Activity委托Window保存数据--> Window委托顶级容器(ViewGroup,可能是DecorView)保存数据--> ViewGroup通知子元素保存数据。这是一种典型的委托模式,典型的委托模式还有View绘制过程
、事件分发
等。
上面被销毁并进行了重建,那什么情况下不会被重建呢?
如果不想被重建,那么可以给Activity指定configChange属性:比如android:configChanges=”orientation”
屏幕旋转的时候不会进行重建,也不会调用onSaveInstanceState和onRestoreInstanceState,而是调用onConfigurationChanged
方法。
配置常见的有三种:locale、orientation、keyboardHidden。
2. 资源内存不足导致优先级低的Activity被杀死
Activity优先级从高到低:
- 前台Activity--正在与用户进行交互的Activity优先级最高。
- 可见但非前台Activity--弹出对话框等导致的Activity可见但无法交互。
- 后台Activity--已经被暂停的Activity,比如执行了onStop(切在后台),优先级最低。
1.2 Activity的启动模式
1.2.1 Activity的LuncherMode
- standard:标准、默认模式,不管在栈中是否存在,都会创建Activity实例在启动它的Activity所在的栈中。需要注意:创建的实例默认会在启动它的Activity所在的栈中,那么,非Activity的Context(ApplicationContext)并不存在所谓的任务栈,所以会报错,解决方法就是为待启动Activity指定FLAG_ACTIVITY_NEW_TASK标志位,启动的时候为其创建一个任务栈,而添加了FLAG_ACTIVITY_NEW_TASK的Flag,实际就变成了以singleTask模式启动。后面会说到Flag。
- singleTop:栈顶复用模式,如果新的Activity在栈顶,则不会创建,同时回调onNewIntent,onCreate、onStart不会被回调,因为Activity没有创建。
- singleTask: 栈内复用模式,单实例模式,只要Activity在一个栈中存在,那多次启动都不会创建Activity,会回调onNewIntent,同时要启动的Activity之上的Activity全部被清除出栈,如果不存在,则重新创建一个任务栈,然后创建Activity放入栈中。
- singleInstance:单实例模式,加强版singleTask,此模式的Activity单独位于一个任务栈中。
1.2.2 Activity的Flags
标记位一般与启动模式相关,会影响Activity的运行状态。
常见的几种Flags:
- FLAG_ACTIVITY_NEW_TASK:其效果等同于为Activity指定“singleTask”的启动模式。
- FLAG_AVCTIVITY_SINGLE_TOP:其效果等同于为Activity设置“singleTop”的启动模式。
- FLAG_ACTIVITY_CLEAR_TOP:清除同一任务栈中位于它上面的Activity,一般与FLAG_ACTIVITY_NEW_TASK配合使用,被启动的Activity如果已存在,那么就会调用onNewIntent方法,如果被启动的Activity使用standard模式,那它连同他之上的都要出栈,会重新创建实例,放入栈顶。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记位的Activity不会出现在历史Activity列表中(Android 多任务管理器查看的时候不会显示),等同于xml中指定Activity属性为:android:excludeFromRecents=”true”;
官方文档:Intent-Flags
1.3 IntentFilter的匹配规则
启动Activity分为两种:显式调用和隐式调用。显式调用明确指定被启动对象的组件信息,包括类名和包名。隐式调用根据过滤信息进行匹配,同时使用隐式和显式时,以显式为主。匹配的信息:action、category、data,可以有多组intent-filter,只有同时匹配一组才能启动目标Activity。
1. action: action的字符串完全一致才算匹配成功,区分大小写,一组intent-filter可以有多个action,匹配一个就算成功。
2. category: category是一个字符串,系统预定义了一些category,Intent可以没有category,如果有,不管几个,都必须和intent-filter过滤规则中的category匹配。不设置可以匹配是因为在startActivity或startActivityForResult的时候会默认添加“android.intent.category.DEFAULT”这个category。
3. data: data的匹配规则与action类似,data由两部分组成:mimeType和URI。mimeType指媒体类型,比如image/jepg、audio/mpeg4-generic和video/*等,可以表示图片文本视频等不同媒体类型,而URI包含的数据比较多,大致结构:
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
结合实例:
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
Scheme: URI的模式,比如http、file,没有指定,其他参数无效,URI无效。
Host: URI的主机,比如www.baidu.com,未指定,其他参数无效,URI无效。
Port: URI中的端口号,比如80,指定了Scheme和Host,Port才有效。
Path、pathPattern、pathPrefix: 表示路径信息,path、pathPattern表示完整路径,可以包含通配符*
,表示0或多个任意字符,pathPrefix表示路径的前缀信息。
例子:
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"/>
隐式意图注意内容:
通过隐式意图启动Activity的时候,可以做一下判断,看是否有 Activity匹配到隐式意图,不做判断,匹配不到可能会出现ActivityNotFoundException异常,判断的方式:PackageManager的resolveActivity
或着Intent的resolveActivity
,另外PackageManager提供了queryIntentActivities方法,返回值是所有匹配的Activity信息。
public abstract List<ResolveInfo> queryIntentActivities(Intent intent,@ResolveInfoFlags int flags);
public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags);
需要注意的是,第二个参数使用MATCH_DEFAULT_ONLY
仅仅匹配在intent-filter中声明了android.intent.category.DEFAULT的category的Activity,意义在于,只要不返回null,那么startActivity一定会成功。
还有一类action和category比较重要:
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
二者共同作用,表明这是程序入口的Activity并且出现在系统应用列表中,二者缺一不可。