任务栈
- android任务栈又称为Task,它是一个栈结构,具有后进先出的特性,用于存放我们的Activity组件。
- 我们每次打开一个新的Activity或者退出当前Activity都会在一个称为任务栈的结构中添加或者减少一个Activity组件,因此一个任务栈包含了一个activity的集合, android系统可以通过Task有序地管理每个activity,并决定哪个Activity与用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。
- 在我们退出应用程序时,必须把所有的任务栈中所有的activity清除出栈时,任务栈才会被销毁。当然任务栈也可以移动到后台, 并且保留了每一个activity的状态. 可以有序的给用户列出它们的任务, 同时也不会丢失Activity的状态信息。
- 需要注意的是,一个App中可能不止一个任务栈,某些特殊情况下,单独一个Actvity可以独享一个任务栈。还有一点就是一个Task中的Actvity可以来自不同的App,同一个App的Activity也可能不在一个Task中。
任务栈信息可以通过 adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'
指令来查看 栈信息.
四种启动模式
-
standard
: 默认的启动模式,每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否存在.Activity的启动三回调(onCreate()->onStart()->onResume())都会执行。 如在一个任务栈中,连续调用3次MainActivity
,那么任务占中就会存在三个MainActivity
记录. -
singTop
: 栈顶复用模式. 当准备启动的Activity已经位于任务栈栈顶时,不会再创建一个新的Activity,此时栈顶Activity的onNewIntent
方法会被回调.当准备启动的Activity不在任务栈的栈顶存在时,(无论在栈中存在或者在非栈顶),都会重新创建 Activity. -
singleInstance
: 单实例模式. 系统为声明为singleInstance
模式的Activity,单独使用一个任务栈存放.即一个Activity对应一个任务栈,且一个Activity最多存在一个实例. 后续请求都不会创建Activity,只会调用其onNewIntent
回调. -
singleTask
: 栈内复用模式. 在这种模式下,如果Activity已经存在某个任务栈中,多起启动该Activity都不会被重建,只会调用其onNewIntent
方法,并将其移动到栈顶位置.
singleTask
模式
当启动Activity时,如果Activity已经存在于 任务栈的非栈顶位置, 任务栈中该Activity之上的 Activity将会被全部移除,只保留该Activity以及其一下的Activity.
即 具有 clearTop
的效果.
当启动Activity的任务栈B在后台时,此时任务栈A中的Activity启动了,任务栈B中的 singleTask
模式的Activity,则任务B栈的 被启动Activity会clearTop
到栈顶位置,然后整个任务栈B重返至前台.
引用 官方示例来说明 :
前台栈中的栈顶Activity2 启动了 位于后台栈的 ActivityY,此时 后台栈中的 ActivityY和ActivityX 都会被移动至前台栈中,回退两次才能回到 Activity2.
taskAffinity
属性
taskAffinity
是AndroidManifest
中 Activity标签的属性,表示任务栈的名称.该属性可以为 Activity指定任务栈.
当Activity的启动模式为
standard,singletop
模式时,taskAffinity
属性将失效, Activity的将被加入到 启动它的那个Activity所在的栈.一个例外,如果在入口Activity中指定taskAffinity
属性,则可以生效,因为此时应用刚启动,还没有指定的任务栈(默认的任务栈是以包名来命名的).当Activity的启动模式为
singleTask,singleInstance
模式时,taskAffinity
属性将在Activity被启动的时候指定任务栈,如果任务栈不存在则会创建一个taskAffinity
命名的任务栈存放启动的Activity.
注意事项
如果被启动的Activity是
standard,singletop
模式时, Activity将被加入到启动该Activity的任务栈中.如果被启动的Activity是
singleTask,singleInstance
模式时,如果指定taskAffinity
属性,则Activity被加入到 指定的栈任务栈中,未指定则被加入 以包名命名的任务栈.singleInstance
模式的Activity,虽然可能被加入到名字相同的栈,但是由于其特殊性,虽然栈名相同但是不属于同一个栈.使用 application来启动Activity时, application没有所在的 任务栈.如果没有指定
FLAG_ACTIVITY_NEW_TASK
,将会有如下报错.
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
Activity中的 Flags
-
FLAG_ACTIVITY_NEW_TASK
: 为Activity指定singleTask
模式,相当于在xml中指定启动模式一样,除了application来启动Activity时,在xml指定singleTask
是无效的,一定要在intent中指定该标志. -
FLAG_ACTIVITY_SINGLE_TOP
: 指定singleTop
模式. -
FLAG_ACTIVITY_CLEAR_TOP
: 具有此标记的Activity,当它启东市,在同一个任务栈中的所有位于它上面的Activity都将被移除任务栈. -
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
: 设置了该标志,该Activity所在的任务栈将不会出现在 多任务列表(最近使用列表)中.相当于设置了android:excludeFromRecents="true"
.
IntentFilter匹配规则
intent的启动有两种方式,显式调用和隐式调用.
显示调用,我们需要明确的指定被启动的Activity的组件信息,Activity的class对象.
隐式调用则不需要明确的知道 被调用的Activity信息,而是通过IntentFilter
来指定action,category,data
过滤规则配合来启动Activity.
Intent只有同时匹配了 action
规则,category
规则 和 data
规则,才能成功的启动目标Activity.
一个 IntentFilter
中可以设置多个 action
,多个category
和多个data
.
action
intent中必须设置 action
, 且能过匹配 IntentFilter
中的任意一个 action
就算匹配成功.
category
intent中如果含有 category
,那么所有的category
都能在IntentFilter
中匹配上,intent中也可以不显式的设置category
.
在调用 startActivity
或者startActivityForResult
时,系统会为intent自动添加 android.intent.category.DEFAULT
标志,所以如果要想隐式的调用Activity,就必须在 IntentFilter
中添加 <category android:name="android.intent.category.DEFAULT" />
规则.
data
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string" />
data由两部分组成, URI 和 mimeType(媒体类型).
URI结构 : <scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
mimeType : image/jpeg
,video/*
等.
如果IntentFilter
中设置了 data
属性.则intent中必须设置 setData
或者setType
或者setDataAndType
.setData
或者setType
方法的属性会相互覆盖.各自的方法会将对方属性设置为null
.
当data
中指设置了 mimeType
时,可以使用 scheme
为 file或content
类型来设置data
.
如果需要同时指定URI
和mimeType
,需要使用intent的setDataAndType
方法.
隐式调用,可达性检查
隐式intent启动之前,我们可以先检查 目标Activity的可达性,避免出现 android.content.ActivityNotFoundException: No Activity found to handle Intent
的错误.
使用 intent的 intent.resolveActivity(packageManager)
和 packageManager的packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
来检查, 返回 null
则表示找不到匹配的Activity.