前言

此外,这篇文章的背景来自于周五的时候,有一个小伙伴私聊我一个问题。说实话让我“颇为震惊”:

这里在分析启动模式的基础上,或回答他的问题,或验证他的猜想。主要集中在这几个地方,大家也可以在看文章的时候先问问自己能不能回答出啦:

问题一:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(1)

验证部分:2.3

问题二:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(2)

这是一个很有趣的现象,估计大家都没有注意到吧~解答部分:2.1

问题三:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(3)

这里的答案是上面问题的一个延伸,也会在2.1部分中解释

问题四:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(4)

这个问题应该也是众多小伙伴感到“有悖常识”的地方,其实官方文档已经解答了这个问题。解答部分2.4

正文

一、理解概念

在学习Ativity的过程中,Task的概念多少是我们无可回避的概念。从官方文档中我们基本能够理解明白Task和Stack的关系…

一张很经典的图:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(5)

Activity以Task的概念聚合在一起,而且无论是单一Task中的多个Activity还是多个Task混在一起,它们都是以Stack形式所包含在一起。

但是,不知道有多少小伙伴在dumpsys activity之后陷入了另一个疑惑:TaskRecord是啥?

1.1、Stack、Task、TaskRecord?

其实Task和TaskRecord是同一个概念。TaskRecord是framework层的一个类。它就是Task这个抽象概念的代码实现。让我们看一个dumpsys的图:

使用命令:adb shell dumpsys activity

android启动模式和应用场景 你真的了解Android的四种启动模式吗(6)

使用命令:adb shell dumpsys activity activities | sed -En -e '/Stack #/p' -e '/Running activities/,/Run #0/p'

android启动模式和应用场景 你真的了解Android的四种启动模式吗(7)

这两幅图是同一个Activity栈的场景,但是可以看出来TaskRecord和Task是同一个概念

1.2、launchModel

启动模式,这个咱们都很熟悉,张口就来:

关于它们的概念,这里就不做累述,毕竟复制粘贴也没有任何意义。(后文会详细阐明四种模式taskAffinity以及各种Flags的作用)但是这里特别贴一张官方的图,大家感受一下:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(8)

这代表了Google对singleTask以及singleInstance的态度,也就是说这俩种模式并不是设计给主流Activity的,因此使用这俩种模式前需要真正的理解它们。

二、从demo中彻底理解

demo很简单定义了5个Activity:

<activity android:name=".test.TestMainLauncherActivity" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".test.TestSingleInstanceLauncherActivity" android:launchMode="singleInstance"/> <activity android:name=".test.TestTopAffinityLauncherActivity" android:launchMode="singleTop" android:taskAffinity="app.mdove"/> <activity android:name=".test.TestSingleTaskLauncherActivity" android:launchMode="singleTask" /> <activity android:name=".test.TestSingleTaskAffinityLauncherActivity" android:taskAffinity="app.mdove.singletask" android:theme="@style/MyAppTheme" android:launchMode="singleTask" />

2.1、理解taskAffinity

首先,咱们看一下文档对android:taskAffinity的描述:

与 Activity 有着相似性的任务。从概念上讲,具有同一相似性的 Activity 归属同一任务(从用户的角度来看,则是归属同一“应用”)。任务的相似性由其根 Activity 的相似性确定。

相似性确定两点内容 :

仅从文档的描述来看,似乎有些雨里雾里。所以接下来咱们通过实际代码来确认taskAffinity的含义。

这里我先贴结论

1、taskAffinity单独设置在standard、singleTop是没有任何意义的。因为taskAffinity的效果依赖启动Activity时使用FLAG_ACTIVITY_NEW_TASK标签。(也就是上文官网提到的那一条)

2、以taskAffinity模式启动成功后,新的Activity会处在新的Task。并且由此启动的Activity(无taskAffinity)皆处于此Task。

接下来咱们用户实际效果验证这个结论(操作路线):

不知道大家能否从脑海里,构建出Task的结构?这里我贴一下dumpsys的图:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(9)

简单解释一下:

1、看一下Task #21,由于我们启动TestTaskAffinityLauncherActivity时没有增加FLAG_ACTIVITY_NEW_TASK,因此它的Task并没有变化。

2、在Task #22中,TestTaskAffinityLauncherActivity通过FLAG_ACTIVITY_NEW_TASK直接进入了新的Task中,并且后续启动的Activity也和它“共处一室”。

咱们看一个很有趣的现象(解答问题二)

如果我们通过FLAG_ACTIVITY_NEW_TASK taskAffinity启动了一个Activity1,并且用这个Activity1启动一个普通的Activity2,如果此时在Activity2上启动Activity1,会发现没有任何反应!!

其实不能说是没反应,“系统的反应”是吧Activity1所在的task移到了前台。说时候这个细节我一直没有注意到,要不是这个小伙伴提醒,我可能很长一段时间都不会注意到这个问题。

复现了这个现象的时候,我也很懵逼。第一想法是翻一翻文档,是不是文档早有提示…然后大失所望,并没有发现任何关于此现象的解答。

既然文档没有解释,那咱们就翻一翻源码…不过我刚点击去FLAG_ACTIVITY_NEW_TASK,有看到了大段的注释,其中有这么一段话:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(10)

这里注释着:如果这个Task中run了你要satrt的Activity,那么将不会起一个新的Activity,而是将此Task移至前台。这个注释正是我们遇到的现象…

而对于FLAG_ACTIVITY_NEW_TASK来说,它本质是去找要启动的taskAffinity所声明的Task,如果没有,它的处理方式和普通起一个Activity没有本质区别,所以就不会做特殊处理。

那么问题来了:为啥要这么设计的?实话我也不知道…我猜应该是为了提供这么一种能力,毕竟PM的想象力是无限的。(如果我们想要在这种情况下,显示要start的Activity,别忘了还有FLAG_ACTIVITY_CLEAR_TOP呢)

2.2、理解singleTask

singleTask下,只有设置了taskAffinity才会为对应启动的 Activity创建一个新的Task。并且后续的Activity同进入这个Task。

我相信这个大家都很熟悉…

android启动模式和应用场景 你真的了解Android的四种启动模式吗(11)

2.3、理解singleInstance

大家对singleInstance,应该都比较清晰。因为比较特殊:永远独享一个Task

这里我们看俩个有意思的特性:

1、以通过singleInstance启动的Activity会默认带上FLAG_ACTIVITY_NEW_TASK。因此如果通过singleInstance启动带有taskAffinity的singleTop,会起新的Task。

android启动模式和应用场景 你真的了解Android的四种启动模式吗(12)

这里有一个细节:仅带有FLAG_ACTIVITY_NEW_TASK,不带有taskAffinity。是会将启动的Activity放到根栈里。因此这里会出现一个细节:

那就是如果由singleInstance启动一个Activity,由于会放入根栈,那么此时根Task会被拉到前台,那么此时back键就会显示根Task下面的Activity,也就是如下的情况:

此时back掉TestMainLaunncherAcctivity,显示的仍然是TestMainLaunncherAcctivity。因为他们同属一个#43的栈。

android启动模式和应用场景 你真的了解Android的四种启动模式吗(13)

2.4、FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_CLEAR_TOP = singleTask?

很多博客/资料都会提到一个说法:FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_CLEAR_TOP = singleTask。

但是这句话并不全对,这不是我说的,而是文档说的:

android启动模式和应用场景 你真的了解Android的四种启动模式吗(14)

注意红线圈住的内容:也就是意味着用这俩个FLAG对于standard模式来说,并不会触发onNewIntent(),而是销毁重建。

尾声

OK,这篇文章到此就结束了…所以叭叭整了很多内容,但是从Google的态度也能看出来,singleTask已经singleInstance是一种慎重使用的提示。

似乎standard和singleTop已经可以满足我们的需求了…不过,似乎真的有点小瞧四种启动模式了…

,