精华内容
下载资源
问答
  • 2021-06-03 11:40:25

    launchMode,通俗点说,就是定义了Activity应该如何被launch的。那么这几种模式的区别以及应用场景,会有何不同呢?谷歌是基于什么原因设计这几种模式的呢?这几种模式背后的工作原理是什么呢?

    1. 任务和返回栈

    在讲解launchMode之前,先说说任务(Task)和返回栈(Back Stack,有些译作回退栈、任务栈)这两个概念。

    A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack—the back stack—in the order in which each activity is opened.

    任务是指当完成一个特定的工作时与用户交互的一系列Activity。这些Activity按照打开的顺序存放在一个栈中,即返回栈。

    通过定义可以知道,Activity会被按照打开的顺序存放。不难猜想,这种存放方式,是为了方便回退操作,也就不难解释为什么要用栈去存放。

    当用户点击启动app的时候,这个app的返回栈就会跑到前台,如果这个返回栈不存在的话,就会创建一个。当前Activity启动另一个Activity的时候,新的Activity就会入栈,在栈顶。如果用户点击返回按钮,当前的Activity就会出栈并销毁,之前的Activity就会被resume,如果栈为空,就会被销毁掉。栈中的Activity永远都不会被重新排序。

    返回栈根据是否在前台,可以分为在前台显示的返回栈,和置于后台的返回栈。其中,置于后台的返回栈中所有的Activity都处于stop状态,用户可以手动的去切换前后台的返回栈状态。

    当系统内存不足时,系统会优先销毁处于后台的Activity。那么问题来了。后台销毁Activity的优先级是怎样的呢?是将一个返回栈中的Activity都销毁了过后,再去销毁另一个,还是说,只是单纯的按照Activity来销毁回收呢?

    1.1 任务管理

    任务以及返回栈的管理,可以通过一系列的参数设置来进行,包括我们本文讲解的launchMode,也是任务管理的一种方式。

    1.1.1 taskAffinity

    TaskAffinity即任务相关性,标识一个Activity所需要的返回栈的名字。默认情况下是包名。设置了相同taskAffinity属性的Activity会被放进同一个栈中。一个返回栈的相关性(affinity)是由这个栈的根Activity的相关性(affinity)决定的。

    taskAffinity属性主要与singleTask或allowTaskReparenting结合使用,在其他情况下,这个属性没有作用。这是为什么呢?

    1.1.2 allowTaskReparenting

    它的主要作用是Activity的迁移,从一个栈迁移到另一个栈,这个迁移跟Activity的taskAffinity有关。

    1.1.3 clearTaskOnLaunch

    这个属性用来清除回退栈中除了根Activity的所有Activity,只对根Activity起作用。当设置为true时,每次重新进入app,只会看到根Activity。

    1.1.4 finishOnTaskLaunch

    这个属性与clearTaskOnLaunch相反,它是将本Activity移除出去,而不影响其他的Activity。

    1.1.5 alwaysRetainTaskState

    这个属性的作用是保存返回栈的状态,只对根Activity起作用。正常情况下,系统清理一个返回栈,会将根Activity之上的所有Activity都清除掉。设置该属性后,系统会保存当前的状态。

    2. 启动模式

    启动模式主要的作用是什么呢?根据上面对任务及返回栈的介绍,它的作用是定义,一个新的Activity实例如何与当前的任务相关联。它本身是任务的管理方式。

    启动模式有两种定义方式,manifest里定义和intent flag的方式。一种是类似配置式的,一种是代码层面的。可以大致推测,肯定是带么层面的优先级高一些,但是代码方式劣处就是不启动Activity就无法设置。Android中这种一般提供动态以及静态方式的,套路都大致相同,一些区别各种优劣等。

    其中manifest设置与intent flag中都包含对方没有的方式。这也是两者的一个区别。

    2.1 launchMode

    此处的launchMode专指Activity的launchMode属性。其中有四种方式,这四种方式想必大家也都很清楚了,在这里我不详细展开了。

    2.1.1 standard

    最常见的一种模式,Activity的默认模式,每次启动该模式的Activity,都会被重新创建,可以从属不同的任务,也可以在一个任务中被创建多次。

    它的应用场景特别广泛, 一般不是特殊需求的话,都会去使用这种模式。

    2.1.2 singleTop

    如果在当前任务的栈顶,系统会调用Activity的onNewIntent()方法而不是重新创建一个新的实例。当用户点击返回键时,当前Activity会被出栈,而不是会退到onNewIntent()之前的状态。

    它的应用场景也有一些。例如搜索页面,每次打开,搜索一些结果,点击详情页面,然后继续搜索。在比方说,通过通知打开的页面,如果该页面存在,则更新,如果不存在,则创建。

    2.1.3 singleTask

    该模式只允许系统中存在一个该Activity的实例,如果当前实例不存在,则创建,如果已经存在,则将该实例之上的Activity全部出栈,走onNewIntent()。

    singleTask适合作为程序入口点,当通过其他方式调用app时候,不会反复创建主页面。例如一般情况下的MainActivity,其他app调用的时候。

    2.1.4 singleInstance

    这种模式与singleTask十分类似,区别在于,持有该Activity的任务中只能包含一个Activity即它本身。

    singleInstance适合需要与程序分离开的页面,例如闹钟的响铃界面,与闹钟的设置相分离。再例如系统的拨号界面。

    2.2 Intent flags

    此处讨论的是通过代码方式进行设置,常见的有如下三种方式。

    2.2.1 FLAG_ACTIVITY_NEW_TASK

    使用一个新的返回栈来启动Activity,跟上面讨论的singleTask类似

    2.2.2 FLAG_ACTIVITY_SINGLE_TOP

    跟上面讨论的singleTop类似

    2.2.3 FLAG_ACTIVITY_CLEAR_TOP

    这种方式是上面讨论的launchMode中不存在的,它与singleTop的区别是,当已存在该实例了,会将它之上的Activity都出栈。

    它经常与FLAG_ACTIVITY_NEW_TASK组合使用,可以达到singleTask的作用。

    3. 回到问题

    几种模式的区别以及应用场景,会有何不同呢?

    答案见上面关于launchMode

    谷歌是基于什么原因设计这几种模式的呢?

    关于这个问题,我们先倒着来推理,即从使用场景去考虑,一般状况下,我们打开一个页面,不在意是否是唯一,这个是最常见的需求,因此有了standard模式,这种也是默认的模式。当我们用搜索页面,当最顶层是搜索页面的时候,我不希望再打开一个搜索页面,于是有了singleTop模式。当从其他App调用我们的app的时候,我只希望只显示一个主页面时,于是有了singleTask。关于singleInstance模式,则是希望与当前的页面分离。

    但是,我觉得谷歌并不能列举出所有的场景,例如,我希望打开一个页面,记录当前的路径,例如a->b->c,这种场景下,四种模式里面没有包含。

    如果从正面去推导的话,几种启动模式是任务及返回栈的管理。根据在栈中的状态,大致可以分为如下几类:

    最常见的出栈入栈(standard)

    当前栈中唯一(singleTask)

    全局唯一(singleInstance)

    栈顶唯一(singleTop)

    是不是很明晰了,有没有其他的出现形式?肯定有的,例如栈底唯一,栈中唯一。但是这种方式可以等同于当前栈中唯一啊。

    我们是否可以推导出,谷歌是根据唯一性,来将启动模式分为这几种呢?intent flags则作为辅助的一些操作,例如部分出栈等等。当然这些也只是我的推测,不一定准确,哈哈。

    这几种模式背后的工作原理是什么呢?

    见任务及返回栈

    内存回收的方式,是以Activity还是以任务作为基准回收?

    目前已知的状况时,如果返回栈置于后台,当内存不足的时候,如果不设置alwaysRetainTaskState属性的话,会将除了根Activity的所有Activity销毁掉。可以确定是以返回栈为基准来进行回收。

    taskAffinity属性为什么与singleTask一起使用才生效?

    可以将Activity的launchMode根据是否在栈中唯一分为两类

    standard、singleTop

    singleTask、singleInstance

    第一类因为其唯一性,肯定是与taskAffinity不兼容的。singleInstance创建的栈中只能包含本身,默认情况下都会单独创建一个栈,指定与否都会单独创建,因此设置没有意义。而singleTask则是当前栈中唯一,适合作为根Activity,创建一个新的栈,这也是为什么taskAffinity只能对根Activity起作用的缘故。

    4. 最后

    我写的内容不一定正确,一些问题的解释也是根据我看到的资料来推到出来的,例如Activity为什么会有四种启动模式,如果大家有准确地答案,希望告知。另外,文中错误的地方,也希望指正。

    最后,感谢大家的浏览,希望对您有所帮助。

    更多相关内容
  • 我们今天要讲的是Activity的四种launchModelaunchMode在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里...
  • LaunchMode

    2015-10-27 23:47:15
    LaunchModeDemo android四种启动模式Demo。 配合此文http://blog.csdn.net/u011726984/article/details/49456287 风味更佳。
  • 主要介绍了Android LaunchMode四种启动模式详细介绍的相关资料,这里对launchmode的使用方法进行了详解及启动模式的比较,需要的朋友可以参考下
  • Activity一共有以下四种launchMode: 1.standard:默认,每次使用Intent跳转到目标Activity时都创建一个新的实例。坏处是每次进入都要创建新的实例,执行OnCreate方法。 2.singleTop:如果要跳转的目标Activity...
  • Activity的launchMode

    2019-03-20 04:17:37
    NULL 博文链接:https://zxl-ong.iteye.com/blog/875779
  • NULL 博文链接:https://mypyg.iteye.com/blog/919643
  • 启动模式的重要性 Android编程中经常涉及到页面的切换,启动一个新的页面(或者说Activity)的时候需要为其指定合适的“启动模式”。指定的启动模式不合适,会出现类似下面这种奇怪的效果: ...这些情况都是我在自己写...
  • Activity LaunchMode解析

    千次阅读 2020-11-03 15:44:51
    Activity LaunchMode LaunchMode Android中Activity的启动模式有四种: standard(默认选项) singleTop singleTask singleInstance 可以在AndroidManifest.xml中通过指定activity标签的android:launchMode属性来改变...

    Activity LaunchMode

    LaunchMode

    Android中Activity的启动模式有四种:

    • standard(默认选项)
    • singleTop
    • singleTask
    • singleInstance

    可以在AndroidManifest.xml中通过指定activity标签的android:launchMode属性来改变:

    <activity android:name=".SecondActivity"
        android:launchMode="singleTop"/>
    

    如果不指定launchMode属性,那么activity会使用默认(standard)模式来运行

    standard

    standard是Activity的默认属性,Android在启动standard的Activity时遵循以下规则:

    1. 如果是点击图标进行启动,则会新创建一个task,然后将这个Activity压入这个task
    2. 如果是使用startActivty进行启动,则会将这个Activity压入启动它的那个Task中
    3. 默认情况下,Activity是多例模式,即一个Activity可以同时存在多个实例,也可以同时存在于多个task

    详细说明

    Activity的standard模式在自己App内部通常表示没有特殊的式样要求,遵循系统默认情况即可, 但是在多个App之间使用的时候需要注意, 它会被压入启动他的那个App的task中, 例如: 在电话App中选择添加联系人操作, 此时启动的是联系人App的添加联系人Activity,此时添加联系人Activity会被压入到电话App中,在用户看来,就好像是这个页面是属于电话App的.
    因为从整个业务流程来看, 用户此时的心理预期是:

    整个操作都属于电话App的流程, 而与联系人app无关

    此时Android系统的表现为:

    1. 添加联系人页面的入场动画为Activity进入的动画,而不是task切换的动画(未指定动画的情况下)
    2. 当用户点击多任务键时, 在后台也看不到联系人App, 只能看到电话app
    3. 添加联系人页面中点击返回键,页面会回退到电话App的页面
    4. 如果此时用户手动打开了联系人App, 并再次添加联系人, 会得到一个全新的添加联系人页面, 并且与电话App中的`添加联系人页面没有冲突

    singleTop

    singleTop整体的行为与standard一致,区别如下:

    • 当Activity在入栈时,singleTop会检测当前task的栈顶Activity,如果当前的栈顶Activity与要压入的Activity是同一个Activity时,那么就不会创建Activity的实例,而是会直接使用当前的栈顶Activity,并且会调用Activity的onNewIntent方法,将新的Intent传入

    singleTop只会检测栈顶的Activity,保证Activity在栈顶唯一,但是Activity还是可以以多例存在的

    例如:
    AActivity被设置为singleTop,AActivity启动自己,或其它应用继续启动AActivity,此时:

    1. AActivity在栈顶, 则不会创建AActivity,而是会调用AActivity的onNewIntent方法
      在栈顶

    2. AActivity不在栈顶, 则会重新创建一个AActivity的实例, 并执行其onCreate方法
      不在栈顶

    singleTask

    被标记为singleTask的Activity可以让该Activity在被别的Activity启动时,不会进入启动他的Task中,而是会在属于它自己的Task里创建,并放在栈顶, 然后把整个Task一起拿过来,压在启动Task上.

    是否在当前Task中启动Activity,主要看taskAffinity:

    • 如果新的Activity的taskAffinity和当前Task相同,就继续在当前Task启动
    • 如果不同,就换到别的Task,如果有则利用那个task,如果没有就创建一个,找到对应task后:
      • 如果目标Task里没有目标Activity,创建Activity,入栈
      • 如果目标Task中已有目标Activity,那么就将这个栈中在目标Activity上面的Activity全部销毁,让目标Activity出现在栈顶,然后调用他的onNewIntent()方法

    singleInstance

    整体行为与singleTask类似,但是有以下区别:

    • 启动singleInstance的Activity的时候,不管新Activity和当前Task的taskAffinity是否一样,都会创建他独有的Task
    • 在singleInstance的Activity上启动其他Activity时,无论二者的taskAffinity是否一样,都不允许其他Activity进入当前task

    singleTask的Activity只会单独存在在一个task中, 并且这个task中不能有其他的Activity,强调其独占

    taskAffinity

    taskAffinity代表的是task的相关性,可以认为是Android想要将同一个逻辑链条中的Activity放入统一个Task中,在Android中,不同task的taskAffinity是可以相同的

    指定TaskAffinity

    可以在AndroidManifest中通过android:taskAffinity属性指定Activity所在task的taskAffinity

    taskAffinity

    • 如果Activity没有指定taskAffinity,那么就会使用application的taskAffinity
    • 如果Application没有指定taskAffinity, 那么就会使用应用的包名作为taskAffinity

    最近任务

    按下Android中的多任务键,会进入多任务页面,多任务页面中显示的实际上是所有的task,并不是应用,当有多个task具有相同的taskAffinity是,最近任务列表里只会显示最近新展示过的一个,所以如果标记了singleInstanse的Activity,完全有可能不显示在多任务页面中

    例如:
    AActivity标记为singleInstance,AActivity启动了本应用的BActivity,此时AActivity和BActivity就会分别处于不同的task栈中,但是这两个栈的taskAffinity都是应用的包名(默认情况下),此时点击多任务页面,只能看到BActivity的task,AActivity的task由于taskAffinity与BActivity的taskAffinity相同而被隐藏起来了

    Task要不要在最近任务列表里显示

    上述的显示规则,也可以被FLAG改变:

    • FLAG_ACTIVITY_NEW_DOCUMENT: 添加了这个Flag的Activity,每个都会独立显示在最近任务里,但是关闭后,不会在最近任务里保留残影
    • FLAG_ACTIVITY_RETAIN_IN_RECENTS: 可以修改上面的规则,让Activity在最近任务里保留残影
    • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:这个Activity从一开始就不会显示在最近任务里,切走之后就再也切不回来了

    返回键

    当点击Android中的返回键时,Android会按照以下逻辑进行执行:

    1. 对当前task执行出栈的操作,显示出当前task的上一个Activity
    2. 如果当前task执行出栈后,没有其他Activity,则销毁这个task,但是并不会销毁其在多任务页面的快照
      1. 当前task销毁后,如果有其他前台task,则会切换到上一个前台task
      2. 当前task销毁后,如果没有其他前台task,则会直接显示Home

    所谓的前台task,指的是再Activity运行过程中,使用startActivity的方式开启了其他的task,这个时候这些新启动的task就会以栈的形式组织起来,当一个task销毁后,就可以显示上一个task

    但是,如果用户按下了home键,或者多任务键后, 这些以栈形式组织起来的task就会被打散,用户是无法通过返回键再回到上一个task中.

    例如:

    1. 在便签应用中点击网址,此时会启动浏览器,这个时候前台的task有两个:便签App的task和浏览器的task,每个task中只有一个Activity此时如果按下返回键,会将浏览器的Task销毁,然后页面回到了便签App
    2. 在便签应用中点击网址,此时会启动浏览器,此时按下多任务键,然后再回到浏览器,此时,前台的task就只剩下浏览器App一个了,这个时候按下返回键,Android会销毁浏览器的Task,然后回到Home

    更完整的场景分类

    Activity的启动,可以有很多分类方式:

    打开的时候放进哪个Task

    1. 默认情况下(standard)以及singleTop,是属于当前所属的逻辑链条,即直接放入当前的task,而不去检查taskAffinity

    2. 如果是singleTask,就需要判断当前Activity和目标Activity的taskAffinity是否相同,相同就会放在当前task,不同就放入别的Task,而且在放进别的Task的时候,也会先尝试找到一个和目标Activity的taskAffinity匹配的Task,找到就进入这个Task,找不到才会创建新的Task

      总结起来,singleTask其实是个很简单的规则: 查找taskAffinity相同的Task,找到就让Activity进入,找不到就创建新的Task

      而更本质上, 其实Activity启动的时候,所有的launchMode都会 穿换成对应的Intent的Flag(0个或多个都有可能),其中 singleTask会被转换成好几个Flag的叠加,其中一个就是 Intent.FLAG_ACTIVITY_NEW_TASK,如果在startActivity 的时候,在Intent里添加上这个Flag:

      Intent intent = new Intent(this,MainActivity.class) ;
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      startActivity(intent);
      

      那么新Activity启动时的入栈规则就会和配置了singleTask的 Activity一样.

      但是我们实际上也可以跳过这个搜索过程,如果在Intent里除了 FLAG_ACTIVITY_NEW_TASK之外,再配置上 FLAG_ACTIVITY_MULTIPLE_TASK:

      Intent intent = new Intent(this,MainActivity.class) ;
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
              Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
      startActivity(intent);
      

      那么Activity就会跳过搜索,直接创建一个新的Task来吧这个 Activity放在栈底

    3. 如果launchMode是singleInstance,并且目标Activity和当前的Activity不同,就放进新的单独的Task;

      • 另外,如果当前的Activity已经是singleInstance,那不管目标Activity是什么,只要二者不是同一个Activity,就会把目标Activity放进别的Task

    打开的时候要不要清掉顶部的Activity

    前面提到FLAG_ACTIVITY_NEW_TASK的入栈规则和singleTask一样,同时singleTask会被转换成多个Flag,而不只是FLAG_ACTIVITY_NEW_TASK,singleTask的行为规则除了入栈方面,还有清理顶部: 如果目标Activity已经在目标Task,但是不在栈顶,就会把上面的Activity全部清理掉.而这个清理顶部也是singleTask所对应的Flag之一:FLAG_ACTIVITY_CLEAR_TOP,需要将FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_CLEAR_TOP配合在一起使用,才能达到类似singleTask那样的清理顶部的效果,否则只有FLAG_ACTIVITY_CLEAR_TOP的话,Activity入栈时永远会在栈顶,就没有顶部可以清理了

    打开时要不要复用已有的Activity

    在singleTask的规则里, 如果目标Task里已经有了目标Activity,目标Activity顶部的其他Activiy会被清理掉,让activity回到栈顶;而Intent.FLAG_ACTIVITY_CLEAR_TOP做的也是清理顶部的活,看起来好像一样, 但是singleTask除了清理顶部,还会复用栈内的目标Activity,调用它的onNewIntent方法,这点和singleTop是一样的,而Intent.FLAG_ACTIVITY_CLEAR_TOP并不会复用Activity,他的默认规则是:将目标Activity顶部的其他Activity,以及目标Activity一起,全部清理掉,然后重新启动目标Activity到栈顶.

    如果想在清理其他Activity的时候,别把目标Activity一起清掉,需要加上另一个flag:

    FLAG_ACTIVITY_SINGLE_TOP

    这个flag就是singleTop,所对应的唯一flag.

    而singleTask其实对应了3个flag:FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_CLEAR_TOP+FLAG_ACTIVITY_SINGLE_TOP,所以singleTask实际上就是这三个flag共同作用的规则:

    1. 让Activity寻找和自己taskAffinity相同的Task(FLAG_ACTIVITY_NEW_TASK)
    2. 清理掉目标Activity顶部的其他Activity(FLAG_ACTIVITY_CLEAR_TOP)
    3. 复用栈中已有的目标Activity(FLAG_ACTIVITY_SINGLE_TOP)

    实战选择

    • standard和singleTop多用于App内部
    • singleInstance:多用于开放给外部App来共享使用
    • singleTask:内部交互和外部交互都会用得上
    • 启动Activiy尽量不要设置singleTask,因为这样用户每次点击图标的时候,都会将task中其他的Activiy全部杀死.即使配置了clearTackOnLaunch = false也不能解决
    展开全文
  • NULL 博文链接:https://kevindan.iteye.com/blog/1991346
  • 1.launchMode介绍 android:launchMode共有四种模式可与 Intent 对象中的 Activity 标记(FLAG_ACTIVITY_* 常量)协同工作,以确定在调用 Activity 处理 Intent 时应执行的操作。这些模式是: standard singleTop ...

    1.launchMode介绍

    android:launchMode共有四种模式可与 Intent 对象中的 Activity 标记(FLAG_ACTIVITY_* 常量)协同工作,以确定在调用 Activity 处理 Intent 时应执行的操作。这些模式是:

    • standard
    • singleTop
    • singleTask
    • singleInstance

    默认模式是“standard”。

    这些模式可分为两大类:

    • standard和singleTop 为一类,可多次进行实例化

    使用standard或singleTop启动模式的 Activity 可多次进行实例化。实例可归属任何Task,并且可位于 Activity 堆栈中的任何位置。通常,它们会启动到名为 startActivity() 的任务中(除非 Intent 对象包含 FLAG_ACTIVITY_NEW_TASK 指令,在此情况下会选择其他任务 — 请参阅 taskAffinity 属性)。

    • singleTask和singleInstance 为另一类。

    singleTask和singleInstance 模式Activity 只能启动Task。它们始终位于Activity 堆栈的根位置。此外,设备一次只能保留一个 Activity 实例,即一次只允许一个此类任务。

    具体的差别如下图:
    在这里插入图片描述

    standard和singleTop模式

    standard和singleTop模式只有一处不同:在singleTop模式如果目标任务的 Activity 堆栈顶部已有一个 Activity 实例,则该实例会(通过调用 onNewIntent())接收新的 Intent,此时不会创建新实例

    在其他情况下(例如,如果“singleTop”Activity 的某个现有实例虽在目标任务内,但未处于堆栈顶部,或者虽然位于堆栈顶部,但不在目标任务中),系统会创建新实例并将其送入堆栈,和standard一样。

    singleTask和singleInstance模式

    singleTask和singleInstance模式同样只有一处不同:singleTask模式的Activity 允许其他 Activity 成为其Task的一部分。该 Activity 始终位于其任务的根位置,但其他 Activity(必然是“standard”和“singleTop”Activity)可以启动到该任务中。另一方面,singleInstance模式的Activity 不允许其他 Activity 成为其Task的一部分,它是Task中唯一的 Activity。如果它启动另一个 Activity,则系统会将该 Activity 分配给其他任务,就如同 Intent 中包含 FLAG_ACTIVITY_NEW_TASK 一样。

    2.launchMode实现原理分析

    android里面的Activity体系结构(3)_ActivityRecord和Activity状态变化分析说明 有介绍,在
    ActivityStarter里面startActivityUnchecked对launchMode进行处理

    2.1 startActivityUnchecked

    核心逻辑在startActivityUnchecked这个里面处理,分2类:

    • singleTask和singleInstance模式:处理在getReusableIntentActivity,不会重复创建Activity,然后reusedActivity.getTaskRecord()找到可以复用的Task

      • singleInstance模式:使用mRootActivityContainer.findActivity查找,因为在singleInstance模式下,只能存在一个Activity实例
      • singleTask模式:使用mRootActivityContainer.findTask找到最合适的Task,因为在singleTask模式下,可能在多个Task中存在Activity实例
    • standard和singleTop模式:singleTask和singleInstance模式处理完后,然后处理standard和singleTop模式。调用topStack.topRunningNonDelayedActivityLocked(mNotTop)获取当前top Activity,用作standard和singleTop分别处理

      • singleTop模式:如果非空且为mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0,调用deliverNewIntent,直接启动
      • standard模式:非以上3种场景,直接创建一个新的Activity

    下面2.2、2.3、 2.4 分别介绍几种模式的处理,建议对着源代码一起看,方法很长,下面贴的代码有裁剪

        private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                ActivityRecord[] outActivity, boolean restrictedBgActivity) {
    
            // 复用Activity
            ActivityRecord reusedActivity = getReusableIntentActivity();
            // 1.有可复用的Activity
            if (reusedActivity != null) {
               
                if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                        || isDocumentLaunchesIntoExisting(mLaunchFlags)
                        || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
                    // 找到可以复用的Task
                    final TaskRecord task = reusedActivity.getTaskRecord();
    
                    // In this situation we want to remove all activities from the task up to the one
                    // being started. In most cases this means we are resetting the task to its initial
                    // state.
                    final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
                            mLaunchFlags);
    
                    // The above code can remove {@code reusedActivity} from the task, leading to the
                    // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
                    // task reference is needed in the call below to
                    // {@link setTargetStackAndMoveToFrontIfNeeded}.
                    if (reusedActivity.getTaskRecord() == null) {
                        reusedActivity.setTask(task);
                    }
                    //  要启动的Activity位于栈顶,直接调用deliverNewIntent,使用onNewIntent启动
                    if (top != null) {
                        if (top.frontOfTask) {
                            // Activity aliases may mean we use different intents for the top activity,
                            // so make sure the task now has the identity of the new intent.
                            top.getTaskRecord().setIntent(mStartActivity);
                        }
                        deliverNewIntent(top);
                    }
                }
    
                mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded
                        (false /* forceSend */, reusedActivity);
    
                reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
    
                final ActivityRecord outResult =
                        outActivity != null && outActivity.length > 0 ? outActivity[0] : null;
    
                // When there is a reused activity and the current result is a trampoline activity,
                // set the reused activity as the result.
                if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
                    outActivity[0] = reusedActivity;
                }
    
                if (reusedActivity != null) {
                    setTaskFromIntentActivity(reusedActivity);
    
                    if (!mAddingToTask && mReuseTask == null) {
                        // We didn't do anything...  but it was needed (a.k.a., client don't use that
                        // intent!)  And for paranoia, make sure we have correctly resumed the top activity.
                        resumeTargetStackIfNeeded();
                        if (outActivity != null && outActivity.length > 0) {
                            // The reusedActivity could be finishing, for example of starting an
                            // activity with FLAG_ACTIVITY_CLEAR_TOP flag. In that case, return the
                            // top running activity in the task instead.
                            outActivity[0] = reusedActivity.finishing
                                    ? reusedActivity.getTaskRecord().getTopActivity() : reusedActivity;
                        }
    
                        return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
                    }
                }
            }
            
            // 2. reusedActivity为空
            // If the activity being launched is the same as the one currently at the top, then
            // we need to check if it should only be launched once.
            final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
            final ActivityRecord topFocused = topStack.getTopActivity();
            final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
            final boolean dontStart = top != null && mStartActivity.resultTo == null
                    && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
                    && top.mUserId == mStartActivity.mUserId
                    && top.attachedToProcess()
                    && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                    || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
                    // This allows home activity to automatically launch on secondary display when
                    // display added, if home was the top activity on default display, instead of
                    // sending new intent to the home activity on default display.
                    && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
            if (dontStart) {
                // For paranoia, make sure we have correctly resumed the top activity.
                topStack.mLastPausedActivity = null;
                if (mDoResume) {
                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                }
                ActivityOptions.abort(mOptions);
                if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                    // We don't need to start a new activity, and the client said not to do
                    // anything if that is the case, so this is it!
                    return START_RETURN_INTENT_TO_CALLER;
                }
    
                deliverNewIntent(top);
    
                // Don't use mStartActivity.task to show the toast. We're not starting a new activity
                // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
                mSupervisor.handleNonResizableTaskIfNeeded(top.getTaskRecord(), preferredWindowingMode,
                        mPreferredDisplayId, topStack);
    
                return START_DELIVERED_TO_TOP;
            }
    
           
            boolean newTask = false;
            final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                    ? mSourceRecord.getTaskRecord() : null;
    
            // Should this be considered a new task?
            int result = START_SUCCESS;
            if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                    && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
                newTask = true;
                result = setTaskFromReuseOrCreateNewTask(taskToAffiliate);
            } else if (mSourceRecord != null) {
                result = setTaskFromSourceRecord();
            } else if (mInTask != null) {
                result = setTaskFromInTask();
            } else {
                // This not being started from an existing activity, and not part of a new task...
                // just put it in the top task, though these days this case should never happen.
                result = setTaskToCurrentTopOrCreateNewTask();
            }
            if (result != START_SUCCESS) {
                return result;
            }
    
            mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
                    mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
            mService.getPackageManagerInternalLocked().grantEphemeralAccess(
                    mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
                    UserHandle.getAppId(mCallingUid));
            if (newTask) {
                EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
                        mStartActivity.getTaskRecord().taskId);
            }
            ActivityStack.logStartActivity(
                    EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord());
            mTargetStack.mLastPausedActivity = null;
    
            mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
                    false /* forceSend */, mStartActivity);
    
            mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
                    mOptions);
            if (mDoResume) {
                final ActivityRecord topTaskActivity =
                        mStartActivity.getTaskRecord().topRunningActivityLocked();
                if (!mTargetStack.isFocusable()
                        || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                        && mStartActivity != topTaskActivity)) {
                    // If the activity is not focusable, we can't resume it, but still would like to
                    // make sure it becomes visible as it starts (this will also trigger entry
                    // animation). An example of this are PIP activities.
                    // Also, we don't want to resume activities in a task that currently has an overlay
                    // as the starting activity just needs to be in the visible paused state until the
                    // over is removed.
                    mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
                    // Go ahead and tell window manager to execute app transition for this activity
                    // since the app transition will not be triggered through the resume channel.
                    mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
                } else {
                    // If the target stack was not previously focusable (previous top running activity
                    // on that stack was not visible) then any prior calls to move the stack to the
                    // will not update the focused stack.  If starting the new activity now allows the
                    // task stack to be focusable, then ensure that we now update the focused stack
                    // accordingly.
                    if (mTargetStack.isFocusable()
                            && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
                        mTargetStack.moveToFront("startActivityUnchecked");
                    }
                    mRootActivityContainer.resumeFocusedStacksTopActivities(
                            mTargetStack, mStartActivity, mOptions);
                }
            } else if (mStartActivity != null) {
                mSupervisor.mRecentTasks.add(mStartActivity.getTaskRecord());
            }
            mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
    
            mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTaskRecord(),
                    preferredWindowingMode, mPreferredDisplayId, mTargetStack);
    
            return START_SUCCESS;
        }
    
    2.2 getReusableIntentActivity 处理singleTask和singleInstance模式

    在此方法里面,使用isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)判断 ,处理singleTask和singleInstance模式

    方法里面的注释写的很清楚,不用单独作说明:

    • 针对singleInstance模式,使用mRootActivityContainer.findActivity,因为在此模式下,只会存在一个Activity实例,通过Activity名称直接在ActivityStack里面查找
    • 针对singleTask模式,mRootActivityContainer.findTask,因为在此模式下,可能在多个TaskRecord里面存在,所以需要找到合适的TaskRecord

    从实现上来看,也体现了在singleTask和singleInstance模式里面的唯一差别

        /**
         * Decide whether the new activity should be inserted into an existing task. Returns null
         * if not or an ActivityRecord with the task into which the new activity should be added.
         */
        private ActivityRecord getReusableIntentActivity() {
            // We may want to try to place the new activity in to an existing task.  We always
            // do this if the target activity is singleTask or singleInstance; we will also do
            // this if NEW_TASK has been requested, and there is not an additional qualifier telling
            // us to still place it in a new task: multi task, always doc mode, or being asked to
            // launch this as a new task behind the current one.
            boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
                    (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                    || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
            // If bring to front is requested, and no result is requested and we have not been given
            // an explicit task to launch in to, and we can find a task that was started with this
            // same component, then instead of launching bring that one to the front.
            putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
            ActivityRecord intentActivity = null;
            if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
                final TaskRecord task = mRootActivityContainer.anyTaskForId(mOptions.getLaunchTaskId());
                intentActivity = task != null ? task.getTopActivity() : null;
            } else if (putIntoExistingTask) {
                if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
                    // There can be one and only one instance of single instance activity in the
                    // history, and it is always in its own unique task, so we do a special search.
                   intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
                           mStartActivity.isActivityTypeHome());
                } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
                    // For the launch adjacent case we only want to put the activity in an existing
                    // task if the activity already exists in the history.
                    intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
                            !(LAUNCH_SINGLE_TASK == mLaunchMode));
                } else {
                    // Otherwise find the best task to put the activity in.
                    intentActivity =
                            mRootActivityContainer.findTask(mStartActivity, mPreferredDisplayId);
                }
            }
    
            if (intentActivity != null
                    && (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
                    && intentActivity.getDisplayId() != mPreferredDisplayId) {
                // Do not reuse home activity on other displays.
                intentActivity = null;
            }
    
            return intentActivity;
        }
    

    在找到可以复用的Activity的时候,找到对应的TaskRecord,清除目标Task里面位于要启动的Activity之上的Activity

    // 找到可以复用的Task
    final TaskRecord task = reusedActivity.getTaskRecord();
    // 清除目标Task里面位于要启动的Activity之上的Activity
    final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,mLaunchFlags);
    

    进行上面的操作后,目标Activity位于TaskRecord的栈顶,直接调用deliverNewIntent,调用top Activity的onNewIntent

    if (top != null) {
        if (top.frontOfTask) {
            // Activity aliases may mean we use different intents for the top activity,
            // so make sure the task now has the identity of the new intent.
            top.getTaskRecord().setIntent(mStartActivity);
        }
        deliverNewIntent(top);
    }
    
    2.3 topRunningNonDelayedActivityLocked 处理singleTop模式

    getReusableIntentActivity方法返回非空时,把singleTask和singleInstance模式处理完。返回空时,先获取当前FocusStack,然后获取top Activity

    在topRunningNonDelayedActivityLocked方法里面,遍历ActivityStack里面的所有TaskRecord

        ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                final TaskRecord task = mTaskHistory.get(taskNdx);
                final ArrayList<ActivityRecord> activities = task.mActivities;
                for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                    ActivityRecord r = activities.get(activityNdx);
                    if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
                        return r;
                    }
                }
            }
            return null;
        }
    

    如果当前要启动的Activity和当前Top Activity是一样的,通过dontStart变量来确认是否需要只启动一次

            final boolean dontStart = top != null && mStartActivity.resultTo == null
                    && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
                    && top.mUserId == mStartActivity.mUserId
                    && top.attachedToProcess()
                    && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                    || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
                    // This allows home activity to automatically launch on secondary display when
                    // display added, if home was the top activity on default display, instead of
                    // sending new intent to the home activity on default display.
                    && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
    

    当启动模式为singleTop模式时,mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP,dontStart为true

            if (dontStart) {
                // For paranoia, make sure we have correctly resumed the top activity.
                topStack.mLastPausedActivity = null;
                if (mDoResume) {
                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                }
                ActivityOptions.abort(mOptions);
                if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                    // We don't need to start a new activity, and the client said not to do
                    // anything if that is the case, so this is it!
                    return START_RETURN_INTENT_TO_CALLER;
                }
    
                deliverNewIntent(top);
    
                // Don't use mStartActivity.task to show the toast. We're not starting a new activity
                // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
                mSupervisor.handleNonResizableTaskIfNeeded(top.getTaskRecord(), preferredWindowingMode,
                        mPreferredDisplayId, topStack);
    
                return START_DELIVERED_TO_TOP;
            }
    

    如果启动模式为singleTop模式,且当前top.mActivityComponent.equals(mStartActivity.mActivityComponent) 即top Activity和要启动的Activity是一致的时候,调用deliverNewIntent,调用onNewIntent方法,不会再创建一个新的Activity

    2.4 处理stander模式

    前面3种模式处理完后,最后处理stander模式。判断是否需要创建一个newTask,来保存启动的Activity

    // Should this be considered a new task?
    int result = START_SUCCESS;
    if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
        && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
        newTask = true;
        result = setTaskFromReuseOrCreateNewTask(taskToAffiliate);
    }       
    

    后面返回START_SUCCESS后,即startActivity返回,后面会再走
    android里面的Activity体系结构(3)_ActivityRecord和Activity状态变化分析说明的 4.2 第2阶段:DeskClock应用进入创建并启动
    创建一个新的Activity

    展开全文
  • Android中LaunchMode详解

    2020-03-25 21:42:45
    LaunchMode的作用和使用方法 一般情况,如果没精神病,LaunchMode是用在Activity上面的,我们就谈谈在Activity上的LaunchMode LaunchMode作用 顾名思义,LaunchMode就是启动模式,啥是启动模式?启动模式意思是使用...

    概念解释
    Task
    Task叫做任务,这个简单,表示我们需要完成的事情,注意,这里我们说的是任务,是个名词,例如要发短信,那我们的任务就是发送一条短信,仅此而已,再例如教官说:”张三,你去吃屎!”,ok,那张三的任务就是吃屎。

    Back Stack
    我们常叫做回退栈,或者是任务栈,这个是什么意思呢?上面我们说过,需要完成任务,那我们就需要使用一系列的Activity来完成,例如发短信,则完成该任务需要如下步骤:

    打开短信主页面MainActivity
    点击添加短信按钮,打开NewSMSActivity
    在NewSMSActivity中编写短信并发送
    以上的任务中涉及到两个Activity,那这两个Activity就存放在这个Back Stack中,又因为Back Stack是栈类型的数据结构,所以上面的步骤在这个Back Stack中的活动顺序如下:

    MainActivity先压栈
    点击添加按钮,NewSMSActivity压栈,
    短信发送完成,点击返回按钮,NewSMSActivity弹栈,回到MainActivity
    在MainActivity点击返回按钮,MainActivity弹栈,此时该Back Stack为空,就返回到Launcher了
    所以我们明白了,这个回退栈其实就是一个存储Activity实例的容器,执行每个Task时,先创建一个Back Stack,在Task执行过程中将所使用的Activity都按照FILO的顺序以此压入这个Back Stack,Task目标完成之后,按下返回按钮时,Back Stack中的Activity按照压栈相反地的顺序以此弹栈,直到栈中没有Activity实例时,进入Launcher。

    由此,我们还可以知道,每个Task和Back Stack是一一对应的关系,一般情况下,每需要执行一个Task时,都至少需要一个Back Stack容器,并且这个容器中都至少会有一个Activity实例。

    LaunchMode的作用和使用方法
    一般情况,如果没精神病,LaunchMode是用在Activity上面的,我们就谈谈在Activity上的LaunchMode

    LaunchMode作用
    顾名思义,LaunchMode就是启动模式,啥是启动模式?启动模式意思是使用不同的模式启动之后,会有不同的属性和表现,举个例子,钢铁侠使用正常模式启动,一般可以秒杀所有小兵小将,但是如果对付发狂的绿巨人浩克,就需要启动超强模式,穿上反浩克装甲,要不然打不过,放到我们Actiivty这里也是一样,既然是配置在Activity上的,那就说明Activity有好几种启动模式,使用不同的启动模式启动的Activity有不同的属性和表现。

    那为啥需要启动模式呢?需求!对,需求是所有东西被建立或者被制造出来的原因,因为我们对Actiivty有不同的需求,举个老生常谈的发邮件例子,邮件主页Activity要求不论怎么打开,打开多少次,就只能有一个主页Activity的实例,对吧,如果有多个实例我们就很麻烦,不知道显示哪个,也不知道要关闭哪个,那这就是个需求,对应这个需求,我们就需要对这个主页Activity设置一种启动模式,不论怎么打开就只有一个实例,这样需求就满足啦。

    LaunchMode使用方法
    按照Android Developer上的说明,LaunchMode有两个使用地方,一个是Mainfest的activity节点下,一个是在startActivity方法的Intent中设置Flag,第二种方法我们后续再讨论,先说说第一种方法。

    要告诉小白的是,LaunchMode要在启动该Activity之前使用才有效,如果Activity都已经启动了,实例都创建完了,再设置什么模式都没用啦。

    第一种使用方式特别简单,在Mainfest中的activity节点中添加android:launchMode即可,如下所示:

    <activity
        android:name=".MainActivity"
        android:label="邮件列表"
        android:launchMode="singleTask">
    </activity>
    1
    2
    3
    4
    5
    6
    有四种launchMode可选,分别是:

    “standard”
    “singleTop”
    “singleTask”
    “singleInstance”
    如果不添加launchMode也可以,默认的launchMode是”standard” 。

    按照Android Developer官方上,他们按照Activity是否可以被实例化多次,把这四个模式分了两个组, “standard” 和”singleTop” 属于可以被实例化多次这个组,他们的实例可以属于任何Task,并且可以位于Back Stack的任何位置,其余两个属于不可被实例化多次这组,他们常用于启动一个Task,所以一个Task只有一个这种实例,并且这个还往往位于Back Stack的最开始。这种分组可以帮助我们初步理解每种启动模式的区别。

    standard
    从现在开始好好讲解这四种启动模式了,首先看看”standard”,有时称为标准模式。

    前面我们说过,Activity默认就是这种模式的,所以你的Activity设置和不设置这个没什么区别,那这种模式的表现是什么样子的呢?

    假设我们有一个”standard”模式的Activity,页面上有个按钮,点击这个按钮就会启动这个Activity自身,由于设置的是”standard”模式,每次启动这个Activity,就会创建这个Activity的新的实例,并依次放入Back Stack,点击一百次就会创建一百个这个Activity的实例。

    “standard”是最简单的模式,也符合我们正常的思维逻辑,所以最好理解,我用简陋的画图工具画了个图,凑合着看吧:

    singleTop
    这种启动模式和标准模式区别不大,只有一点点不同。

    我们已经知道,每个Activity的实例在Back Stack中存储,既然是个Stack数据结构,那么第一个压栈的实例我们叫做栈底实例,因为它将被后进来的实例压在最下面,最后被压入的实例,称作栈顶实例,因为它刚被压人栈中,暂时还没有其他实例在它之上,如果栈中只有一个实例,那这个实例既是栈底实例,也是栈顶实例。

    明白了栈底和栈顶的概念,”singleTop” 就好理解了,当我们启动”singleTop” 模式的Activity时,系统会检查当前的Back Stack的栈顶实例是不是这个”singleTop” 模式Activity的实例,如果是的话,就不创建新的实例了,直接复用这个已经存在的栈顶实例。还拿我们上一个场景为例,如果这个Activity是”singleTop” 模式的,不论你怎么点按钮,Back Stack只会有一个实例,因为栈顶已经存在一个这样的实例,所以不会创建新的了。

    如果上个例子不够明确,我们可以举个比较明显的例子,有两个Activity:ActivityA是标准模式,AvtivityB是”singleTop” 模式,完成一个Task,需要经过以下步骤:

    启动ActivityA,在ActivityA中点击按钮启动ActivityB,
    在ActiivtyB中点击按钮,再次启动ActivityB
    根据之前的描述,当ActivityB的实例第一次被创建时,是位于栈顶的,第二次尝试创建ActivityB的实例之前,由于是”singleTop” 模式,并且栈顶已经有它的实例,就不会再创建新的,这个Task完成之后,Back Stack中只有一个ActivityA的实例和一个ActivityB的实例,简图如下:

    singleTask
    接下来是”singleTask” 模式,还记得文章开头说的两个分组和区别吗?如果你现在还记得,那你就理解了一大半了。

    之前说过,”singleTask” 的Activity在一个Back Stack只会创建一个,这是和前两个模式最大的不同,”singleTask” 和”singleTop”不同之处在于,创建实例时不仅仅只检查栈顶是否已经有实例,还会检查整个Back Stack,只要Back Stack已经存在实例,不论是位于栈顶,栈底还是哪里,都不会创建新的实例。

    “singleTask” 模式不仅不会创建新的实例,还会将从已经创建实例开始到栈顶的所有实例全部清除,并将已经创建的实例放到栈顶,因为只有这样做才会让这个实例可以显示到窗口上。

    还是继续上面的例子,ActivityC的启动模式为”singleTask” ,我们在Back Stack中原来的ActivityA和ActivityB实例之间插入一个ActivityC的实例,形成这样的Back Stack结构之后,如果再次尝试启动ActivityC时,由于ActivityC的实例已经存在,所以复用已经存在的实例,并且清除实例到栈顶的所有实例,所以ActivityB的实例被清除了,此时Back Stack中就只有ActivtyA和ActivtyC的实例。

    singleInstance
    “singleInstance”是最后一个启动模式,则是和其他三个模式都不同的。

    我们之前谈论的启动模式,基本都是在一个Back Stack范围内谈论是否需要重新创建的问题,在这里,我们把范围扩大一下,讨论在多个Back Stack之间重新创建的问题,设置了”singleInstance”模式的Activty,在启动的时候,会脱离于当前Task的Back Stack,在一个新的Back Stack中创建实例。

    还是我们之前的例子,现在把ActivtyA,ActivityB和ActivtyC的实例依次放入Back Stack中,并将这个Back Stack编为1号,然后编写一个ActivityD并将其设置为”singleInstance”模式,此时,如果我们在ActivityC中启动ActivityD,那么ActiviyD的实例将不会位于1号Back Stack中,它将会在一个新的Back Stack中创建一个新的ActivityD实例,简图如下:

    使用场景
    不同的启动模式,适用与应用中的不同应用场景。

    一、standard
    标准模式适用于大多数场景,因为在应用中,我们基本上可以允许用户同时进行多个任务,每个任务操作不同的数据,这样允许创建一个Activty的多个实例,例如新建邮件Activity,如果当前正在新建一个给张三的邮件,此时同时需要创建一个给李四的邮件,此时Activty就要使用标准模式,这样允许创建多个不同的实例,允许创建多封邮件。

    二、singleTop
    singleTop模式,由于其特点是检查栈顶实例,可以用这个特性,防止短时间创建多个实例,例如有个按钮,点击之后打开一个播放视频的Activty,如果用户短时间重复点击,不是singleTop模式的话,就会短时间在Back Stack中出现多个实例,而且每个实例的播放进度不一致,如果是singleTop模式的话,不论打开多少次都没有影响。

    三、singleTask
    这个模式,常用于那些有一定任务,且任务已经进行了一部分,但是忽然又去做别的事情了,等会儿回来还要继续任务的场景,还是播放那个视频的例子,如果正在播放的时候需要去打开新Activty搜索相关视频,搜索完毕又要回到播放页面,那这个页面就比较适合使用singleTask模式

    四、singleInstance
    这种模式较少使用,如果一定要在你的应用中使用这种启动模式,请提前考虑好是否有这个必要,另外,在做一些特殊场景下的应用,比如Launcher的主屏时,可能会使用得到。
    ————————————————
     
    原文链接:https://blog.csdn.net/u012741741/article/details/53784090

    展开全文
  • Activity的四种LaunchMode

    2021-06-07 07:39:57
    任务和返回栈使用android:launchMode="standard|singleInstance|single Task|singleTop"来控制Acivity任务栈。任务栈是一种后进先出的结构。位于栈顶的Activity处于焦点状态,当按下back按钮的时候,栈内的Activity会...
  • 之前遇到了onActivityResult提前调用的问题,说是和activity的启动模式有关,就做了一个Demo将四种启动方式都测试了一下,里面包括 onActivityResult的使用以及在四种不同启动模式下使用效果的不同情况
  • Android中的launchMode

    2021-04-06 23:18:54
    Android中的LaunchMode 写这篇文章主要参考https://www.bilibili.com/video/BV1CA41177Se 和《Android开发艺术探索》这篇文章是我学习LaunchMode的学习笔记 众所周知,Android是使用回退栈来管理Activity的,而根据...
  • 浅谈 Android launchMode和taskAffinity

    千次阅读 2021-11-04 18:44:16
    launchMode 基本概念-Task 任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中。堆栈中的 Activity 永远不会重新排列,只会被送入和...
  • 原创作品,转载请注明出处OActivity 的启动模式有四种,standard、singleTop、singleTask、singleInstance.image.png1.standardstandard是Activity默认的启动模式,在不进行显示指定...android:launchMode="standard...
  • android 如何使用LaunchMode

    千次阅读 2018-07-20 09:49:02
    今天,简单讲讲android的启动模式LaunchMode,以及如何根据自己的需要设置LaunchMode。 这个其实也很简单,我每次使用android studio新建activity时,系统都使用了默认的LaunchMode,即standard模式的LaunchMode。...
  • LaunchMode 有四种,分别为 Standard,SingleTop,SingleTask 和 SingleInstance,每种模式的实现原理一楼都做了较详细说明,下面说一下具体使用场景: Standard: Standard 模式是系统默认的启动模式,一般我们 app...
  • 四种launchMode例程

    2014-06-10 13:20:20
    Android笔记之四种launchMode例程,Activity有四种加载模式: standard singleTop singleTask singleInstance
  • Android活动启动模式4种模式...“标准”“ singleTop”“ singleTask”“ singleInstance”默认模式是“标准”。模式分为两组。 standard和singleTop在一侧,而singleTask和singleInstance在另一侧。...
  • Android launchmode的使用场景SingleTop应用场景:网易新闻。 假设主界面为 MainActivity,显示新闻的界面是 DetailActivity,显然显示任何一条新闻都会使用 DetailActivity,即把新闻内容通过 Intent 传给 ...
  • Android之LaunchMode(启动模式)

    千次阅读 2021-02-23 16:54:03
    Android之LaunchMode(启动模式) 文章目录Android之LaunchMode(启动模式)Activity的4种启动模式1.1 standard ------ 标准模式1.2 singleTop1.3 singleTask1.4 singleInstance Activity的4种启动模式 注意: Activity的...
  • android:launchMode的四种方式

    千次阅读 2018-08-20 11:51:48
    Activity一共有以下四种launchMode:  standard  singleTop  singleTask  singleInstance 1.standard standard模式是默认的启动模式,不用为&lt;activity&gt;配置android:launchMode属性即可,...
  • Activity-LaunchMode

    2018-11-06 10:22:26
    Activity启动模式例子,Standrd,SingleTop,SingleTask,SingleInstance实战实例。
  • 记录下今天碰到的知识点,Android中的android:launchMode属性是android中的启动动模式;取值可为:standard(标准)这种启动模式为标准模式,也是默认模式。每当我们启动一个Activity,系统就会相应的创建一个实例,...
  • 我们今天要讲的是Activity的四种launchModelaunchMode在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 23,395
精华内容 9,358
关键字:

launchMode