生命周期
正常情况
onStart和onResume、onPause和onStop从描述上来看差不多,对我们来说有什么实质的不同呢?
onStart和onStop区分是否可见,onResume和onPause区分是否在前台。
假设当前Activity为A,如果这时用户打开一个新Activity B,那么B的onResume和A的onPause哪个先执行呢?
执行顺序为:
A-onPause
B-onCreate
B-onStart
B-onResume
A-onStop
复制代码
onPause()和onStop()不能做耗时的操作,尽量在onStop中做一些保存操作,使得下一个Activity尽快显示出来。
异常情况
情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建
当系统配置发生改变后,Activity会被销毁,其onPause、onStop、onDestroy均会被调用。
系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法的调用时机是在onStop之前,它和onPause没有既定的时序关系,它既可能在on-Pause之前调用,也可能在onPause之后调用。需要强调的一点是,这个方法只会出现在Activity被异常终止的情况下,正常情况下系统不会回调这个方法。当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时on-SaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。因此,我们可以通过onRestoreIn-stanceState和onCreate方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用时机在onStart之后。
如屏幕旋转会触发这种情况。
系统配置中有很多内容,如果当某项内容发生改变后,我们不想系统重新创建Activity,可以给Activity指定configChanges属性,然后我们自己在Activity的onConfigurationChanged方法中处理自己的逻辑。
情况2:资源内存不足导致低优先级的Activity被杀死
Activity按照优先级从高到低,可以分为如下三种:
- 前台Activity——正在和用户交互的Activity,优先级最高。
- 可见但非前台Activity——比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互。
- 后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。
当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数数据。如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。
启动模式
使用清单文件
在清单文件中声明 Activity 时,您可以使用 元素的 launchMode 属性指定 Activity 应该如何与任务关联。
launchMode 属性指定有关应如何将 Activity 启动到任务中的指令。您可以分配给 launchMode 属性的启动模式共有四种:
- "standard"(默认模式)
- "singleTop"
- "singleTask"
- "singleInstance"
使用 Intent 标志
-
FLAG_ACTIVITY_NEW_TASK :
指定singleTask模式
-
FLAG_ACTIVITY_SINGLE_TOP :
指定singleTop启动模式
-
FLAG_ACTIVITY_CLEAR_TOP :
使其Activity之上的所有Activity都出栈。一般和 FLAG_ACTIVITY_NEW_TASK配合使用,如果Ativity实例已经存在,系统会调用他的onNewIntent。若和standard模式配合使用,那么它和它之上的所有Activity都要出栈,系统会创建新的实例放到栈顶。singleTask默认就有此标记的效果。
-
FLAG_ACTIVITY_EXCLUDE_FROM_RE-CENTS :
具有这个标记的Activity不会出现在历史Ac-tivity的列表中,等同于
android:excludeFromRecents="true"
。
使用方法: intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
任务栈关联
taskAffinity 与 allowTaskReparenting
默认值是包名,需要和allowTaskReparenting或者singleTask配对使用。
当 taskAffinity 和 singleTask 配合使用时,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。
当 taskAffinity 和 allowTaskReparenting 配合使用时,如果allowTaskReparenting为true,应用A启动了应用B的Activty C,按Home键回到桌面,再单击B的桌面图标,显示的是Activity C,而不是显示B的主Activity,C从A的任务栈转移到了B的任务栈中。
实践
上图 B 启动 C ,情况会如何?
清单文件:
<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/AppTheme">
<activity
android:name=".ActivityC"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".ActivityD"
android:launchMode="singleTask"
/>
<activity
android:name=".ActivityA"
android:launchMode="singleTask"
android:taskAffinity="com.task"
/>
<activity
android:name=".ActivityB"
android:launchMode="singleTask"
android:taskAffinity="com.task"
/>
</application>
复制代码
调用顺序 C->D->A->B->C
结果返回栈中是 A->B->C,D被直接出栈。
singleTask模式的Activity切换到栈顶会导致在它之上的栈内的Activity出栈。
IntentFilter 匹配规则
启动Activity分为显示调用和隐式调用,隐式调用需要Intent去匹配IntentFilter中所设置的过滤信息。
例:
<activity
android:name="com.ryg.chapter_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同时匹配action类别、cat-egory类别、data类别才算完全匹配,只有完全匹配才能成功启动目标Activity。
一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。
action 的匹配规则
只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功。
category 的匹配规则
要求Intent中如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。
data 的匹配规则
<data
android:host="string"
android:mimeType="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:port="string"
android:scheme="string"/>
复制代码
URI结构: <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>
例:content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
参考
Android开发艺术探索