任务与活动栈

来源:互联网 发布:暗黑破坏神3 数据库 编辑:程序博客网 时间:2024/04/28 16:19
一个应用程序有很多Activity。Activity之间相互调用。通过传递Intent,还可以调用别的应用程序的Activity。
比如,你的应用程序要发送邮件,只需定义一个有SEND动作的Intent,Android就会在整个系统里查找合适的Activity,然后调用,然后发送。发送邮件的Activity就好像是你的应用程序的一部分一样。
Android把这些Activity都放入一个Task中,以保持它们之间的无缝过渡。所以,所谓的Task就是活动的集合。以栈的形式来管理,起个专有名词叫做Back Stack。

(后台栈、活动栈,这两者是一回事儿。)

HOME屏幕是Task启动的地方。
用户点击了Launcher上的应用程序的图标后,一个应用程序的Task就来到前台。如果还不存在,则先建立一个Task,然后把应用程序的“Main活动”打开,置入栈顶。
若当前Activity启动了第二个Activity,则自身变为停止状态,然后被压到栈的下一级。新启动的Activity置入栈顶。
用户若点击了BACK键,则刚建立的Activity被销毁,然后从栈中弹出。下面的Activity又回到栈顶,恢复成活动状态。
这个栈只有压栈弹栈两种操作,栈中的活动绝对不会因为某种原因去重新排列。
栈中所有东西被弹干净了,Task就消失了。用户会返回到HOME屏幕或前一个Task。
Task在运行时,若用户按了HOME键,或启动了新Task,则老Task被移到后台,所有Activity都成为停止状态。
若用户重新回到HOME屏幕,又选了原来的应用程序,则后台的Task重回到前台,栈顶的Activity恢复为活动状态。

这就是Android的多任务系统。
但后台的Task变多时,系统会在内存吃紧时杀掉其中一些Activity。需要Activity在合适的时机保存所有数据。

因为活动栈从不会重排,若某个Activity比较受欢迎,很多Activity都启动它,就会导致在栈中有多个它的实例。(而不是把栈底的实例移到栈顶)
用户可能会不喜欢这样的操作感受(多按几次BACK键,却能回到同一个Activity的UI界面,会迷惑),可以配置该Activity只能在栈中有一个实例。

系统在内存吃紧时,强行杀掉某些Task的某些Activity,但是却在栈中仍然不弹出。
当用户重新回到该Task的已死的Activity,系统会重建该活动。所以,在onSaveInstanceState()回调方法中保存用户数据是一个好主意。

● 管理Task

你可能希望某个Activity被启动时不是被放到栈顶,而是重新建立一个Task。这样,它在Task栈中总是唯一的。
你也可能希望某个Activity启动时,先从栈底看是否已有被压下的实例,若有则恢复之。
你也可能希望在用户离开某Task时,把活动栈清干净,只剩下一个Root活动(即最先启动的Activity)。

这需要两方面的工作:AndroidManifest.xml的<activity>属性,和startActivity()所用的Intent的标志参数。有了这些东西,Activity就知道自己如何和Task联系。

<activity>主要有下面这些属性可用:
- taskAffinity
- launchMode
- allowTaskReparenting
- clearTaskOnLaunch
- alwaysRetainTaskState
- finishOnTaskLaunch

Intent标志有:
- FLAG_ACTIVITY_NEW_TASK
- FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_SINGLE_TOP

● 亲密度 - affinity
一个Activity启动时,放入哪个Task栈中,是由亲密度来决定的。
默认情况下,一个应用程序的所有Activity拥有相同的亲密度,所以它们会聚集在一个栈里。这个亲密度就是应用程序的包名(Package Name)。
你可以改变一个Activity的亲密度,让不同的应用程序的两个Activity有相同的亲密度,或者同一个应用程序中的Activity有不同的亲密度。
在<activity>元素里定义taskAffinity属性即可。
该属性起作用的条件:
- Intent带有FLAG_ACTIVITY_NEW_TASK标志。系统看到此标志,就会在别的Task里找地方存放新的Activity。此时,就要根据亲密度来寻找。找不到就新建一个Task。
- Activity有一个属性allowTaskReparenting="true"的时候。
举例:一个旅游软件,某个Activity的功能是报告天气的,亲密度和应用中其它的Activity一样,都是默认值,不过,其allowTaskReparenting属性是true。
此时,你的应用程序启动了这个天气报告Activity,那么,它就来到了你的Task栈中。
当那个旅游软件启动了并跑到前台时,该天气报告Activity又跑回去了,回到旅游软件的Task栈里。

● 后台栈的清除
当一个Task在后台呆时间太长时,系统会把栈内的Activity清干净,只剩下Root活动。
如果用户此时回到该Task,那他只能回到Root活动。

有几个属性可以更改这种行为。
- alwaysRetainTaskState 如果设为true,那么Task里所有的活动都留下。
- clearTaskOnLaunch 如果Task的Root活动此属性为true,则每次用户离开该Task,栈都会被清一遍。和上面正相反。
- finishOnTaskLaunch 和上面类似,但只作用于一个Activity,而非整个Task。用户一离开,该Activity立刻销毁。

● Task的入口 - Root活动

<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>
若某Activity设置了上面那样的IntentFilter,其图标和名字将出现在Launcher屏幕上,作为整个应用程序的入口。
用户必须能够退出且能从Launcher回到该Activity,所以singleTask和singleIntance这两个属性,只能用于带ACTION_MAIN和CATEGORY_LAUNCHER配置的Activity。
假设这两个配置缺少了,会出现什么问题呢?
打比方,用户使用Intent启动了一个singleTask的新任务,然后按HOME键把它压入后台。该Activity如何被用户重新选择回来?只有带MAIN和LAUNCHER才行。
原创粉丝点击