Android Activity的四种启动模式介绍

1 启动模式介绍

  启动模式简单地说就是Activity启动时的策略,在AndroidManifest.xml中的标签的android:launchMode属性设置; 启动模式有4种,分别为standard、singleTop、singleTask、singleInstance; 
  Activity的管理方式:任务栈。任务栈采用的结构: “后进先出” 的栈结构。每按一次Back键,就有一个Activity出栈。 
这里写图片描述 
图片来源于:Android基础:最易懂的Activity启动模式详解

1.1 标准模式–standard

这里写图片描述 
  若我意图打开的顺序为B1->B2->B2,则实际打开的顺序为B1->B2->B2

1.2 栈顶复用模式–singleTop

这里写图片描述 
  若我意图打开的顺序为:B1->B2->B2,则实际打开的顺序为:B1->B2(后一次意图打开B2,实际只调用了前一个的onNewIntent方法) 
若我意图打开的顺序为:B1->B2->B1->B2,则实际打开的顺序为:B1->B2->B1->B2。

1.3 栈内复用模式–singleTask

  singleTask:如果要激活的那个Activity在任务栈中存在该实例,则不需要创建,只需要把此Activity放入栈顶,并把该Activity以上的Activity实例都pop(onDestroy()) 
这里写图片描述 
  若我的应用程序中有三个Activity,C1,C2,C3,三个Activity可互相启动,其中C2为singleTask模式,如下:

  • C1->C2 ———-> C1->C2
  • C1->C2->C3 ———-> C1->C2->C3
  • C1->C2->C3->C2 ———-> C1->C2
  • C1->C2->C3->C2->C3->C1———-> C1->C2->C3->C1
  • C1->C2->C3->C2->C3->C1-C2———-> C1->C2

1.4 单例模式–singleInstance

  在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。 
这里写图片描述 
这里写图片描述 
  可看,SecondActivity的Task id不同于FirstActivity和ThirdActivity。这说明SecondActivity确实是存放在一个单独的返回栈里的,而且这个栈中只有SecondActivity这一个活动。 
  按下Back键进行返回,发现ThirdActivity竟然直接返回到了FirstActivity,再按下Back键又会返回到SecondActivity,再按下Back键才会退出程序,这是为什么呢? 
  由于FirstActivity和ThirdActivity是存放在同一个返回栈里的,当在ThirdActivity的界面按下Back键,ThirdActivity会从返回栈中出栈,那么FirstActivity就成为了栈顶活动显示在界面上,因此也就出现了从ThirdActivity直接返回到FirstActivity的情况。然后在FirstActivity界面再次按下Back键,这时当前的返回栈已经空了,于是就显示另一个返回栈的栈顶活动,即SecondActivity。最后再次按下Back键,这时所有返回栈都已经空了,也就自然退出了程序。 
这里写图片描述

2 启动模式应用场景

2.1 标准模式–standard

  普通模式:若我有一个Activity名为A1, 上面有一个按钮可跳转到A1。那么如果我点击按钮,便会新启一个Activity A1叠在刚才的A1之上,再点击又会再新启一个在它之上……。点back键会依照栈顺序依次退出。

2.2 栈顶复用模式–singleTop

  singleTop适合接收消息后显示的页面,(例如QQ接收倒消息后弹出Activity,如果10条消息,总不能一次弹10个Activity,只要复用栈顶显示的页面)。例如:某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次激活同一个新闻内容页面,返回时立刻返回前一个页面。

2.3 栈内复用模式–singleTask

  singleTask适合作为程序入口点,例如:浏览器的首页导航。多个应用程序启动浏览器的首页导航,只会启动一次,其它情况都会走onNewIntent,并且会清空主界面上面的其他页面。

2.4 单例模式–singleInstance

  浏览器BrowserActivity很耗内存,很多app都会要调用它,这样就可以把该Activity设置成单例模式。 
  singleInstance应用场景:闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按 Home 键回桌面;在上午5点59分时,你在微信和朋友聊天;在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以 SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面。 
这里写图片描述

3 taskAffinity(亲和力)

  拥有相同affinity的多个Activity理论同属于一个task,task自身的affinity决定于根Activity的affinity值。先找到这个task,没有再创建了一个task,这个task的affinity就是B的MainActivityB默认的affinity,由于B的MainActivityB的affinity是从Application继承而来,所以当appA启动时会直接找到这个task,而不是创建新的task。 
  affinity在什么场合应用呢?(1)根据affinity重新为Activity选择宿主task(与allowTaskReparenting属性配合工作)(2)启动一个Activity过程中Intent使用了FLAG_ACTIVITY_NEW_TASK标记,根据affinity查找或创建一个新的具有对应affinity的task;(3)在其他情况下没有意义。

4 Intent几种常见的启动标志(flags)

  Activity指定启动模式有两种,第一种是通过manifest指定模式,第二种是在Intent中设置标志位为Activity指定启动模式。区别在于:(1)优先级上第二种方式的优先级比第一种要高,同时存在,以第二种为准。(2)限定范围不同,第一种不能直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,第二种方式无法为Activity指定singleInstance模式。

4.1 FLAG_ACTIVITY_NEW_TASK

  当Intent对象包含这个标记时,系统会寻找或创建一个新的task来放置目标Activity,寻找时依据目标Activity的taskAffinity属性进行匹配,如果找到一个task的taskAffinity与之相同,就将目标Activity压入此task中;如果查找无果,则创建一个新的task,并将该task的taskAffinity设置为目标Activity的taskActivity,将目标Activity放置于此task。 
  注意,如果同一个应用中Activity的taskAffinity都使用默认值或都设置相同值时,应用内的Activity之间的跳转使用这个标记是没有意义的,因为当前应用task就是目标Activity最好的宿主。通过实例进行演示这个特性: 
  (1)普通的跳转:应用A启动应用B的B1Activity,应用B的启动页是SplashBActivity,B1Activity会留在应用A的栈中。所以,A–>B的B1Activity,按home键回到桌面,启动应用B是B的启动页面,启动应用A是B的B1Activity页面。 
  (2)设置Intent.FLAG_ACTIVITY_NEW_TASK标志跳转后:应用A启动应用B的B1Activity,应用B的启动页是B的B1Activity页面,B1Activity转移到了应用B的栈中。所以,A–>B的B1Activity,按home键回到桌面,启动应用B是它的B1Activity页面,启动应用A是A的启动页面。

    //在应用A跳转应用B,
    public void startActivityB(View v){
        Intent intent = new Intent("com.google.test");
        //设置Intent.FLAG_ACTIVITY_NEW_TASK标志
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
            try {
                startActivity(intent);
            } catch (ActivityNotFoundException e) {
                Log.e("TAG","activity not found for");
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

  (3)同理,taskAffinity的实现例子:在应用B的manifest配置allowTaskReparenting=”true”和taskAffinity=”com.example.interviewb”,效果与设置Intent.FLAG_ACTIVITY_NEW_TASK标志一样。

<activity
            android:name=".MainActivityB">
            android:allowTaskReparenting="true"
            android:taskAffinity="com.example.interviewb">
            <intent-filter>
                <action android:name="com.google.test" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.2 FLAG_ACTIVITY_CLEAR_TOP

  清除包含此Activity的Task中位于该Activity实例之上的其他Activity实例。这种行为的 launchMode 属性没有对应的值,只能通过代码设置。 
  (1)已经启动了四个Activity:A,B和C。在C里,我们要跳到A。这样启动A,就会销毁B\C,如果A的启动模式是默认的,则A会销毁,再启动一个新的A,A重新执行onCreate–>onStart。

Intent intent = new Intent(this, A.class);   
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
startActivity(intent);
  • 1
  • 2
  • 3

这里写图片描述 
  (2)如果不想重新再创建一个新的A,则A的Manifest.xml配置成android:launchMode=”singleTop”或者配合FLAG_ACTIVITY_SINGLE_TOP使用,则A不会销毁,只销毁A以上实例,然后A执行onNewIntent–>onReStart–>onStart。利用此原理退出整个程序:启动到A,在onNewIntent()中销毁A,参考:采用FLAG_ACTIVITY_CLEAR_TOP退出整个程序(多activity)

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
  • 1

这里写图片描述 
  (3)配合FLAG_ACTIVITY_NEW_TASK使用,和(2)效果一样。 
  (4)Android 关闭多个视图Intent.FLAG_ACTIVITY_CLEAR_TOP用法

4.3 FLAG_ACTIVITY_CLEAR_TASK

  。此Activity将变成一个新Task中新的最底端的Activity,成为根Activity,所有的之前此Activity实例和包含该实例的Task都会被关闭,这个标识仅仅和FLAG_ACTIVITY_NEW_TASK联合起来才有效果,单独使用和标准模式的效果。

4.4 FLAG_ACTIVITY_SINGLE_TOP

  使用singleTo启动Activity,使用效果与指定android:launchMode=”singleTop”相同。

4.5 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET

  如果一个Intent中包含此属性,则它转向的那个Activity以及在那个Activity其上的所有Activity都会在task重置时被清除出task。当我们将一个后台的task重新回到前台时,系统会在特定情况下为这个动作附带一个FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记,意味着必要时重置task,这时FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET就会生效。 
这里写图片描述 
图片来源于:基础总结篇之三:Activity的task相关 
  这个标记对于应用存在分割点的情况会非常有用。比如我们在应用主界面要选择一个图片,然后我们启动了图片浏览界面,但是把这个应用从后台恢复到前台时,为了避免让用户感到困惑,我们希望用户仍然看到主界面,而不是图片浏览界面,这个时候我们就要在转到图片浏览界面时的Intent中加入此标记。

4.6 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

  这个标记在以下情况下会生效:1.启动Activity时创建新的task来放置Activity实例;2.已存在的task被放置于前台。系统会根据affinity对指定的task进行重置操作,task会压入某些Activity实例或移除某些Activity实例。我们结合上面的CLEAR_WHEN_TASK_RESET可以加深理解。

4.7 FLAG_ACTIVITY_NO_HISTORY

  使用NO_HISTOR启动Activity,当该Activity启动新的Activity后,新的Activity会消失,不会保留在栈中。例如:A–>B,B使用这种模式启动C,C再启动D,D在返回时会finish C,返回到B。经过测试: 
  (1)A–>B,B使用这种模式启动C,C–>D,栈中是A–>B–>C–>D,返回是:DBA。 
  (2)A使用这种模式启动B,B–>C,C–>D,栈中是A–>C–>D(离栈顶隔了一个实例,会清除前一个),返回是:DCA。

5 task相关属性

5.1 android:allowTaskReparenting

  这个属性用来标记一个Activity实例在当前应用退居后台后,是否能从启动它的那个task移动到有共同affinity的task,“true”表示可以移动,“false”表示它必须呆在当前应用的task中,默认值为false。实例在5.1中。

5.2 android:alwaysRetainTaskState

  这个属性用来标记应用的task是否保持原来的状态,“true”表示总是保持,“false”表示不能够保证,默认为“false”。此属性只对task的根Activity起作用,其他的Activity都会被忽略。 
  默认情况下,如果一个应用在后台呆的太久例如30分钟,用户从主选单再次选择该应用时,系统就会对该应用的task进行清理,除了根Activity,其他Activity都会被清除出栈,但是如果在根Activity中设置了此属性之后,用户再次启动应用时,仍然可以看到上一次操作的界面。 
  这个属性对于一些应用非常有用,例如Browser应用程序,有很多状态,比如打开很多的tab,用户不想丢失这些状态,使用这个属性就极为恰当。

5.3 android:clearTaskOnLaunch

  这个属性用来标记是否从task清除除根Activity之外的所有的Activity,“true”表示清除,“false”表示不清除,默认为“false”。同样,这个属性也只对根Activity起作用,其他的Activity都会被忽略。 
  如果设置了这个属性为“true”,每次用户重新启动这个应用时,都只会看到根Activity,task中的其他Activity都会被清除出栈。如果我们的应用中引用到了其他应用的Activity,这些Activity设置了allowTaskReparenting属性为“true”,则它们会被重新宿主到有共同affinity的task中。 
这里写图片描述 
图片来源于:基础总结篇之三:Activity的task相关

5.4 android:finishOnTaskLaunch

  这个属性和android:allowReparenting属性相似,不同之处在于allowReparenting属性是重新宿主到有共同affinity的task中,而finishOnTaskLaunch属性是销毁实例。如果这个属性和android:allowReparenting都设定为“true”,则这个属性胜出。

6 onNewIntent触发时机

6.1 位于栈顶:activity的launchMode为singleTop 、singleTask或者singleInstance

这里写图片描述

6.2 清理其他Activity,被onRestart:activity的launchMode为singleTask或者singleInstance

这里写图片描述

6.3 注意,设置标志跳转并不会触发。

7 参考博客

Android基础:最易懂的Activity启动模式详解 
   
基础总结篇之三:Activity的task相关

Activity的启动模式

Android activity onNewIntent触发时机

转载地址:https://blog.csdn.net/chenliguan/article/details/47264783

猜你喜欢

转载自blog.csdn.net/xialong_927/article/details/80268018