谈一谈 Activity lauchMode 以及 任务栈和返回栈
谈一谈 Activity lauchMode 以及 任务栈和返回栈
Activity 的意思是活动,我们通常称Activity 为 我们用手机看到的页面。
一个页面可以开启另一个页面。比如 页面A->开启页面B->开启页面A 这时候就有两个页面A了,那么到底应该有一个页面A的实例呢,还是两个呢,我认为,Android 为了解决这样问题,有了启动模式的概念。Android的启动模式有四种 (standard,singleTop,singleTask,singleInstance)。
页面A->开启页面B->开启页面C 当按返回回到页面B,再按返回回到页面A ,再按返回回到桌面。 我们通常看到的是这样的现象,这是方面用户的习惯,Android 有了这样的设计,那么怎么去实现呢,就有了任务栈的概念 (我对任务栈的理解是,任务栈是 一组关系密切的 Activity 的集合)。
一:启动模式。
standard 标准启动模式,比如 页面A->页面B(标准)->页面C->页面B(标准),这时候任务栈的结构是,ABCB ,可以看到这个栈中有两个B的实例,这个是标准启动。
singleTop 栈顶复用模式,比如 页面A->页面B(singleTop)->页面B(singleTop),这时候任务栈的情况是 AB ,singleTop会栈顶复用,因为任务栈中已经有 B页面了,而且B页面是singleTop模式,所以再启动B页面时候,不会再创建B页面的实例,而是直接用栈顶的B页面的实例,回调B页面的 onNewIntent方法。思考下, 如果 页面A->页面B(singleTop)->页面C->页面B(singleTop)模式,这时候任务栈栈的情况,ABCB,因为B没有在栈顶,所以不会复用。
singleTask 刚才说的,页面A->页面B(singleTop)->页面C->页面B(singleTop)这时候,任务栈的情况是 ABCB,我们不希望栈中有两个B页面的实例,怎么办,之后就有了 singleTask模式。 页面A->页面B(singleTask)->页面C->页面B(singleTask),此时任务栈就变成了 AB 因为singleTask会将 页面B的实例之上的页面的实例全部销毁。举个实际中常用到的例子吧,比如说,首页->二级页面->三级页面->首页,我们希望复用首页的实例,销毁中间的页面,这时候,用singleTask就很方便了。
singleInstance 这个模式比较特殊,singleInstance实例要求自己在一个任务栈中,这个任务栈中不会有其他的Activity实例。
上面介绍的四种模式,介绍的都是一些概念而且都是在一个任务栈中的使用。下面我们说下有多个应用和多个任务栈的情况。也会详细的介绍下对任务栈的理解。
应用TaskA 页面A->页面B->页面C ->启动应用 TaskB的页面CC(standard模式),查看任务栈的结果(注意CCActivity的taskId和TaskA的页面的 taskId一样,因为这是标准的启动模式)
应用TaskA 页面A->页面B->页面C ->启动应用 TaskB的页面CC(singleTask模式),查看任务栈的结果(注意CCActivity的taskId和TaskA的页面的 taskId不一样)
二. taskAffinity和singleTask的关系
这时候引出 taskAffinity的概念 (下面深入讲一下 taskAffinity和singleTask的关系)。
singleTask是和taskAffinity相关的,自己做了实现 standard模式启动一个Activity(指定和包名不同的taskAffinity),实际上这个Activity对应的taskId和启动它的Activity对应的taskId是一样的。如果是singleTask模式启动的话,这个Activity对应的taskId和启动它的Activity对应的taskId是不一样的。所以我们现在认为taskAffinity是和singleTask相关的。
下面举个例子,TaskB应用启动 AAActivity->BBActivity->CCActivity.注意CCActivity的启动模式是 singleTask。看下图的结果。taskId都是94. 而上一次的实验 同样是 singleTask启动CCActivity,为什么taskId是不同的呢???因为上一次的实验室 taskA 启动的TaskB的CCActivity,taskA对应的taskAffinity默认是自己的包名"com.example.cheng.testtaska",taskB对应的taskAffinity默认是自己的包名"com.example.cheng.testtaskb"。接着说,上一次实验,应用TaskA 页面A->页面B->页面C ->启动应用 TaskB的页面CC(singleTask模式) 这时候,发现CCActivity是singleTask模式启动,这时候检查 TaskA的C页面的taskAffnity和TaskB的CCActivity的taskAffinity是不一样的,所以就开启了一个新的 task。而这次实验,发现BBActivity的taskAffinity和CCActivity的taskAffinity是一样的(都是自己的包名),所以就没有启动新的task。
三,谈谈对任务栈的理解。
standard和singleTop情况,都不会开启一个新的任务栈,这种情况,我们先不考虑(因为一个Activity开启Activity无论是自己的应用的Activity还是别的程序的应用的Activity(都是standard或者singleTop模式),这时候只会有一个任务栈,这种情况下,先不做分析)。
singleInstance 开启的Activity,自己在一个任务栈中,而且这个栈中,只有它自己。所以这种情况,我们也先不分析。
谈一下singleTask和任务栈。
第一步实验,桌面Activity启动应用 TaskB AAActivity->BBActivity->CCActivity。按Home键切换到桌面。
启动应用 TaskA AActivity->BActivitiy->CActivity。这时候,我们看下日志。
分析一下日志,我的猜想是桌面应用启动TaskB的AAActivity和TaskA的AActivity,实际上是以 FLAG_ACTIVITY_NEW_TASK 启动了 AAActivity和AActivity(猜想的,因为他们分别对应着两个 taskId)。
第二步实验,桌面应用 启动 应用 TaskB AAActivity->BBActivity->CCActivity。按Home键切换到桌面。
启动应用 TaskA AActivity->BActivitiy->CActivity ->启动taskB的CCActivity(singleTask模式),我们看下日志。
我们分析一下日志:
TaskB AAActivity->BBActivity->CCActivity按Home键切换到桌面。(此时taskB 任务变成后台任务)
桌面Activity启动应用 TaskA 的AActivity->BActivitiy->CActivity (前台任务),这时候再启动TaskB的CCActivity ,将后台任务切换到了前台。因为是 singleTask启动模式,所以CCActivity不会重建,而是调用onNewIntent.所以看到的日志是 onResume==CCActivity。这里再详细的补充一下,为什么没有新建task,新创建CCActivity,因为SingleTask模式启动一个Activity,首先先会看返回栈中,是否有这个Acitivity,如果有的话,不会创建,直接用,如果没有的话,会创建新的任务栈,创建此Activity。singleTask模式启动的Activity,在返回栈中,只会有一个。singleInstance创建的Activity在返回栈中只会有一个,而且此Activity自己在一个任务栈。
再分析一下返回为什么是这个结果。
TaskA CActivity (前台任务)->TaskB的CCActivity 这时候,将 TaskB的AAActivity->BBActivity->CCActivity 变成了前台任务。所以返回的结果是这样的啦。