一个app一般包含多个activities,每个activity执行不同任务。
Task是与用户交互执行一系列任务的activities集合,这些activities以打开顺序排列在一个栈列表中。
用户点击主页面的图标或者快捷方式,如果此应用从未被执行过,那么会创建一个新的task,主界面activity作为这个task的根activity,当根activity启动其他界面,一个新的activity会被压到task的栈顶并获取焦点。此时根activity仍然在task中保存,当用户按返回键时,当前的activity会被弹出task栈堆并销毁,前一个activity重新resumes(之前UI上的状态数据重新加载)。
Task中的activities顺序永远不会重排,只有push(入栈)和pop(出栈)两种动作,作为栈堆,保持着后进先出的原则。
如图所示,1是task的根activity;1启动2之后,2入栈,1被压到栈底,2在栈顶;2启动3之后,3入栈;返回键,3出栈被销毁,2继续在栈顶。这个task中所有在栈顶的activity都是可见并获取用户焦点的界面。
当所有activities都出栈后,那么这个task也就不存在了。
Task可以作为一个整体单元,可以被整体移动到后台,当用户通过Home键返回主界面并启动新的task。
Note:多个task可以同时在后台,但是有可能被系统在回收内存时销毁。
如下是activities和tasks的默认行为:
当系统销毁后台task中的activities时,这些activities界面用户临时数据会丢失(即便如此,task中的顺序依旧存在),当这些activities重新回到栈顶时,不是通过resume,而是通过recreate。为了避免此类数据丢失,强烈建议通过onSaveInstanceState()来保存临时数据。
有时候你需要一个activity在每次启动时都是重新创建一个新的task,而不是安排到现有的task中;或者你创建activity时,直接使用现有已经创建的activity实例,而不是在栈顶创建新的实例;又或者你想用户离开task后清除根activity之外的其他activities。
你可以通过<activity>的属性和intent的flag来改变默认的行为。
<activity>属性如下:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
intent的flag如下:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
Caution:多数应用不需要改变系统的默认行为,如果你确定需要改变默认行为,那么你需要确保启动、返回等场景下应用的可用性,有可能返回键的行为会与用户的预期有冲突。
启动模式允许你定制一个新的activity实例与当前task的关联,有如下两种方式:
如果A启动B,B能过通过manifest定义关联方式,A也能通过intent flags来定义B的关联方式,如果两个都有定义,那么A的intent flags优先。
Note:两种方式不是对等的,有些模式只能用manifest文件来定义,有些只能用intent flags来定义。
<activity>元素的launchMode属性:
onNewIntent()
而不是创建新的实例。activity可以被实例化多次,每个实例可以属于不同的task,一个task可以有多个实例(栈顶不是此activity的实例的task) 一般来说,返回键总是给用户带来上一个界面,当你启动singleTask模式的activity时,如果此activity实例已经在后台task中存在,那么整个task会被切到前台,此时,返回键是逐个显示刚切到前台的task中的所有activities。如下图所示:
Note:manifest中launchMode属性有可能会被intent flags所覆盖。
FLAG_ACTIVITY_NEW_TASK
与singleTask一致
FLAG_ACTIVITY_SINGLE_TOP
与singleTop一致
FLAG_ACTIVITY_CLEAR_TOP
如果activity实例已经在当前task中,启动时,此实例之上的activities都会被销毁,此实例触发onNewIntent()并处于栈顶。
FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK一般联合使用。
Note:如果launch mode是standard,那么已存在的实例也会被销毁,通过创建新的实例来替换销毁实例的位置,因为standard模式始终通过创建新实例来接收intent。
affinity意味着activity隶属于哪个task。默认的,同一app的所有activities对于彼此有affinity,所以默认的,同一app的所有activities都在一个task中。然而,你也可以修改默认的affinity。不同app中的activities能共享一个affinity,或者同一个app中的activities能定义不同的affinities。
可以通过<activity>的taskAffinity属性来修改affinity,taskAffinity是唯一的字串。
两种使用affinity的场景:
Tip:如果apk包含不止一个“application”,那么可以定义taskAffinity来区分。
当用户离开task很长时间后,系统会清理除根activity之外的所有activities,当用户又重新回到这些task,那么只有根activity才会显示。
用户可以通过如下方式来修改这种默认行为:
alwaysRetainTaskState
如果为true在根activity中,那么上述默认行为不会发生,此task中所有activities都会长时间保持
clearTaskOnLaunch
如果为true在根activity中,那么上述默认行为会在用户离开task后立即发生
finishOnTaskLaunch
与clearTaskOnLaunch类似,但是只针对某个activity,而不是整个task,根activity如果定义属性为true也会被清除
同时定义"android.intent.action.MAIN"
和"android.intent.category.LAUNCHER"的activity
在桌面上有入口图标。
这个非常重要:用户可以离开这个task,也可以通过桌面图标返回这个task,所以定义这两种filiter的activites通常会初始化一个task。
singleTask和singleInstance应当在这种activity上定义,试想一下,singleTask启动的activity在一个新的task中,此时用户按Home键,如果桌面上无入口图标,那这个task将永远无法返回。
如果你真心不想用户能够返回一个activity,可以设置finishOnTAskLaunch属性。