1.1 Activity的生命周期全面分析
1.1.1 典型情况下的生命周期分析
在正常情况下,Activity会经历如下生命周期。
(1)onCreate:表示Activity正在被创建;创建一个Activity时首先执行此方法,在这个方法中我们可以做一些初始化工作。
(2)onRestart:表示Activity正在重新启动;一般当当前的Activity从不可见重新变为可见状态时,此方法会被调用。
(3)onStart:表示Activity正在被启动;此时Activity已经“可见”,但没有出现在前台,无法与用户进行交互。
(4)onResume:表示Activity已经可见了;此时Activity已经可见,已经出现在前台,可与用户进行交互。
(5)onPause:表示Activity正在停止;因为此方法执行时间很短,所以不能做太耗时的操作。
(6)onStop:表示Activity即将停止;执行此方法时可以做一些重量级的回收资源的工作,也不能过于耗时。
(7)onDestroy:表示Activity即将被销毁;做一些回收工作和最终的资源释放。
注意:onStart和onStop是从Activity是否可见这个角度来回调的,而onResume和onPause是从Activity是否位于前台这个角度来回调的,除了这种区别,在实际使用中没有其他明显区别;而用户从当前Activity打开一个新的Activity时,先执行原活动的onPause,再执行新活动的onResume。
1.1.2 异常情况下的生命周期分析
1.资源相关的系统配置发生改变导致Activity被杀死并重新创建
比如说当前Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity就会被销毁并且重建。
Activity被销毁时,其onPause、onStop、onDestroy均会被调用。
同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。
当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。
2.资源内存不足导致低优先级的Activity被杀死
Activity的优先级:前台Activity > 可见但非前台Activity(例如在对话框下的Activity) > 后台Activity。
保证后台有一定优先级:将后台工作放入Service中。
3.如何设置在系统配置改变后,Activity不被重新创建
android:configChanges="orientation"
项目 | 含义 |
locale | 设备的本地位置发生了改变,一般指切换了系统语言 |
orientation | 屏幕方向发生了改变,比如旋转了手机屏幕 |
keyboardHidden | 键盘的可访问性发生了改变,比如用户调出了键盘 |
通过设置后,Activity的确没有重新创建,并且也没有调用onSaveInstanceState和onRetoreInstanceState来存储和恢复数据,取而代之的所系统调用了onConfigurationChanged方法。
1.2 Activity的启动模式
1.2.1 Activity的LaunchMode
四种启动模式
(1)standard:标准模式(默认模式)。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在。
(2)singleTop:栈顶复用模式。在这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。
(3)singleTask:栈内复用模式。这是一种单实例模式,在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和singleTop一样,系统也会回调其onNewIntent。同时singleTask默认具有clearTop的效果。
(4)singleInstance:单实例模式。这是一种加强的singleTask模式,它除了具有singleTask模式的所以特性外,还加强了一点,即当A启动时,系统会为它单独创建一个新的任务栈,然后A独自在这个新的任务栈中。
为Activity指定启动模式
第一种是通过AndroidManifest为Activity指定启动模式:
<activity
android:name="com.ryg.chapter_1.SecondActivity"
android:configChanges="screenLayout"
android:launchMode="singleTask"
android:label="@string/app_name"
/>
另一种情况是通过在Intent中设置标志位来为Activity指定启动模式:
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
AndroidManifest | Intent | |
优先级 | 低 | 高 |
限定范围 | 无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识 | 无法为Activity指定singleInstance模式 |
1.2.1 Activity的Flags
FLAG_ACTIVItY_NEW_TASK
这个标记位的作用是为Activity指定“singleTask”启动模式,其效果和在XML中指定该启动模式相同。
FLAG_ACTIVITY_SINGLE_TOP
这个标记位的作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定该启动模式相同。
FLAG_ACTIVITY_CLEAR_TOP
具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般会和singleTask启动模式一起出现,在这种情况下,被启动Activity的实例如果已经存在,那么系统就会调用它的onNewIntent。如果被启动的Activity采用standard模式启动,那么它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈顶。singleTask启动方式默认具有此标记位的效果。
1.3 IntentFilter的匹配规则
1.3.1 action(动作测试)的匹配规则
action的匹配要求Intent中的action存在且必须和过滤规则中的其中一个action相同,这里需要注意它和category匹配规则的不同。另外,action区分大小写,大小写不同字符串相同的action会匹配失败。
1.3.2 category(类别测试)的匹配规则
category是一个字符串。category的匹配规则要求Intent中如果含有category,那么所以的category都必须和过滤规则中的其中一个category相同。换句话说,Intent中如果出现了category,不管有几个category,对于每个category来说,它必须是过滤规则中已经定义了的category。
为了Activity能够接收隐式调用,就必须在intent-filter中指定“android.intent.category.DEFAULT”这个category。
1.3.3 data(数据测试)的匹配规则
data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent中必须也要定义可匹配的data。
data由两个部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、 视频等不同的媒体格式,而URI包含参数如下:
参数 | 解释 |
Scheme | URI的模式,比如http、file、content等,如果URI没有指定scheme,那么整个URI的其他参数也是无效的 |
Host | URI的主机名,比如www.baidu.com,如果host未指定,结果同scheme |
Port | URI的端口号,比如80,仅当URI中指定了scheme和host参数的时候port参数才是有意义的 |
Path/pathPatten/pathPrefix | 表示路径信息 |
举例:
过滤规则如下
<activity
android:name="com.ryg.chaprter_1.ThirdActivity"
android:configChanges="screenLayout"
android:label="@string/app_name"
android:launchMode="singleTask"
android:taskAffinity="com.ryg.task1">
<intent-filter>
<action android:name="com.ryg.charpter_1.c"/>
<action android:name="com.ryg.charpter_1.d"/>
<category android:name="com.ryg.category.c"/>
<category android:name="com.ryg.category.d"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
完全匹配的Intent
Intent intent = new Intent("com.ryg.charpter_1.c");
intent.addCategory("com.ryg.category.c");
intent.setDataAndType(Uri.parse("file://abc"), "text/plain");
startActivity(intent);