Activity的深入理解

来源:互联网 发布:中信银行网络贷款利率 编辑:程序博客网 时间:2024/06/07 05:11

Activity异常状态时的生命周期

当Activity在异常情况下需要重新创建时,系统会默认保存当前的Activity视图结构,并且在Activity重启后为我们恢复这些数据,那么针对某一个特定的View,系统能回复哪些数据呢?
通过View的源码可以看到。

关于保存和回复View层次结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说它很可能是DecorView。最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层。父容器委托子元素去处理一件事情,这种思想在Android中有很多应用,比如View的绘制过程、事件分发等都是类似的思想。至于数据恢复过程也是类似。

onSaveInstanceState只会在Activity即将被销毁并且有机会重新显示的情况下才会调用它。
系统只有在异常终止的时候才会调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,其它情况不会触发这个过程。

这里写图片描述

Activity关于SingleTask启动模式

SingleTask启动模式:栈内复用模式,单实例模式,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,回调onNewIntent方法。具体是当一个singaleTask模式的Activity A请求启动时,系统会现寻找存在它想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例后把A放入栈中。如果存在它所需的任务栈,那么看栈中是否存在A,如果存在则清空A上面的任务栈,另A到栈顶,否则创建A并入栈。如果不存在想要的任务栈,但是A存在于不想要的任务栈中,那么仍然通过TaskAffinity制定想要的任务栈启动时,仍然会创建想要的任务栈,然后创建A并入栈。

什么是Activity所需的任务栈呢?取决于TaskAffinity:翻译为任务相关性。
这个参数标识了Activity所需任务栈的名字,默认情况下,所有Activity的TaskAffinity的名字为应用的packageName。TaskAffinity主要和singleTask后者allowTaskReparenting属性配对使用,在其他情况下没有意义。任务栈分为前台任务栈和后台任务栈。

当TaskAffinity和allowTaskReparenting结合的时候,当一个应用A启动了应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true的话,那么当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。

根据实际测试,当要从B应用启动某个Activity C,并且它设置了allowTaskReparenting属性为true时,launchMode不设置时,那么Home按键回到桌面,点击B应用,打开的不是B的首界面而是Activity C.

Common是A应用,OneSDK是B应用,蓝色是Activity C,红色是B应用的首界面

这里写图片描述

当要从B应用启动某个Activity C,并且它设置了allowTaskReparenting属性为true时,launchMode设置为singleTask时,那么Home按键回到桌面,点击B应用,打开的不是B的首界面而是Activity C.

这里写图片描述

如果不设置B应用的Activity C,那么相同的操作打开的是B应用的首界面,此时查看任务栈,A应用的任务栈最上层是Activity C,B应用的最上层是首界面。

Common是A应用,OneSDK是B应用,蓝色是Activity C,红色是B应用的首界面

Common是A应用,OneSDK是B应用,蓝色是Activity C,红色是B应用的首界面

当即通过AndroidManifest设置launchMode,同时在Intent中指定了启动模式时,代码中的启动模式优先级更高,也就是Intent方式为准。

但是Intent无法为Activity设置为singelInstance模式,在AndroidManifest无法设置Activity为FLAG_ACTIGITY_CLEAR_TOp标识。

测试实例

定义以下Activity

        <activity            android:name="com.derek.demo.MainActivity"            android:configChanges="keyboard|orientation|screenSize"            android:label="@string/app_name"            android:launchMode="singleTop"            android:screenOrientation="sensor" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <activity            android:name="com.derek.demo.MainActivity.SecondActivity"            android:configChanges="keyboard|orientation|screenSize"            android:label="@string/app_name"            android:launchMode="singleTask"            android:screenOrientation="sensor"            android:taskAffinity="com.derek.task1" >        </activity>        <activity            android:name="com.derek.demo.MainActivity.ThirdActivity"            android:configChanges="keyboard|orientation|screenSize"            android:label="@string/app_name"            android:launchMode="singleTask"            android:screenOrientation="sensor"            android:taskAffinity="com.derek.task1" >        </activity>

将SecondActivity和ThirdActivity都设成singleTask并指定它们的taskAffinity属性为com.derek.task1,注意这个taskAffinity属性的值为字符串,且中间必须包含包名分隔符”.”。然后进行如下操作:在MainAcitiviy中点击按钮启动SecondActivity,在SecondActiviy中点击按钮启动ThridActivity,在ThridActivity中单击按钮又启动MainActivity,最后再在MainActivity中单击按钮启动SecondActivity,现在按下back键,然后看到那个Activity?
答案是回到桌面。

Activity的Flags

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有这个标记的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于XML中指定Activity的属性android:excludeFromRecents= "true"

IntentFilter的匹配规则

判断隐式启动Activity的时候,可以做一下判断,查看是否有Activity能够匹配我们的Intent。

PackageManager提供了两个方法如下:

public abstract List<ResolveInfo> queryIntentActivities(Intent intent,int flags);public abstract ResolveInfo resolveActivity(Intent intetn,int flags);

第二个参数flag需要使用MATCH_DEFAULT_ONLY这个标记为,来仅匹配那些在intent-filter中声明了

<category android:name="android.intent.category.DEFAULT" />

第一个方法返回所有匹配的Activity,第二个方法只返回最佳匹配的一个Activity