我觉得理解了Android四大启动模式,直到被打脸

前言

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

  • 一、问题看起来很简单,关于Activity启动模式...可是的确里边的细节触及了个人知识盲区。
  • 二、这个小伙伴发了一个详细的pdf文档,看起来也是常常总结~点赞这种学习态度~

这里在分析启动模式的基础上,或回答他的问题,或验证他的猜测。主要集中在这几个地方,你们也能够在看文章的时候先问问本身能不能回答出啦:shell

问题一: app

验证部分:2.3ide

问题二: 学习

这是一个颇有趣的现象,估计你们都没有注意到吧~解答部分:2.1ui

问题三: spa

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

问题四: 3d

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

正文

1、理解概念

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

一张很经典的图:

Activity以Task的概念聚合在一块儿,并且不管是单一Task中的多个Activity仍是多个Task混在一块儿,它们都是以Stack形式所包含在一块儿。

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

1.一、Stack、Task、TaskRecord?

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

使用命令:adb shell dumpsys activity

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

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

1.二、launchModel

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

  • standard
  • singleTop
  • singleTask
  • singleInstance

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

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

2、从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.一、理解taskAffinity

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

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

类似性肯定两点内容 :

  • Activity 更改父项后的任务(请参阅 allowTaskReparenting 属性,这个属性比较有意思,可是我们先按下不表)
  • 以及经过 FLAG_ACTIVITY_NEW_TASK 标记启动 Activity 时,用于容纳该 Activity 的任务。

仅从文档的描述来看,彷佛有些雨里雾里。因此接下来我们经过实际代码来确认taskAffinity的含义。

这里我先贴结论

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

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

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

  • TestMainLauncherActivity启动TestTopAffinityLauncherActivity

  • 而后在TestTopAffinityLauncherActivity上启动TestMainLauncherActivity

  • 在TestMainLauncherActivity上经过FLAG_ACTIVITY_NEW_TASK标签,启动TestTopAffinityLauncherActivity

  • 最后由TestTopAffinityLauncherActivity启动TestMainLauncherActivity

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

简单解释一下:

一、看一下Task #21,因为咱们启动TestTaskAffinityLauncherActivity时没有增长FLAG_ACTIVITY_NEW_TASK,所以它的Task并无变化。

二、在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,有看到了大段的注释,其中有这么一段话:

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

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

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

2.二、理解singleTask

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

我相信这个你们都很熟悉...

2.三、理解singleInstance

你们对singleInstance,应该都比较清晰。由于比较特殊:永远独享一个Task

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

一、以经过singleInstance启动的Activity会默认带上FLAG_ACTIVITY_NEW_TASK。所以若是经过singleInstance启动带有taskAffinitysingleTop,会起新的Task。

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

那就是若是由singleInstance启动一个Activity,因为会放入根栈,那么此时根Task会被拉到前台,那么此时back键就会显示根Task下面的Activity,也就是以下的状况:

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

2.四、FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP = singleTask?

不少博客/资料都会提到一个说法:FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP = singleTask

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

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

尾声

OK,这篇文章到此就结束了...因此叭叭整了不少内容,可是从Google的态度也能看出来,singleTask已经singleInstance是一种慎重使用的提示。

彷佛standard和singleTop已经能够知足咱们的需求了...不过,彷佛真的有点小瞧四种启动模式了...

我是一个应届生,最近和朋友们维护了一个公众号,内容是咱们在从应届生过渡到开发这一路所踩过的坑,以及咱们一步步学习的记录,若是感兴趣的朋友能够关注一下,一同加油~

我的公众号:咸鱼正翻身
相关文章
相关标签/搜索