Activity任务栈 launchMode
发布日期:2021-06-30 21:21:37 浏览次数:3 分类:技术文章

本文共 8153 字,大约阅读时间需要 27 分钟。

Activity任务栈

        Activity任务栈的使用以及意思  

         <!-- standard默认的模式 以栈的形式存储到应用容器中 -->

            <!-- singleTop 如果该Activity在栈顶 就不会重新启动了

                比如我们设置一个登陆页面    如果用户点击登陆点击两次那么会加载俩次登陆页面如果使用     singleTop 

                如果点击两次那么他只会 启动一次Activity

                singleTop 他一直在栈顶

             -->

            <!-- 

 

        4.singleTask

          当打开属性为singleTask的Activity时,先判断Activity是否在栈内存在,如果不存在则创建;如果存在,则要判断是否该        Activity上面有其他Activity,如果有,则直接关闭其他的Activity。

            应用场景:应用的主页

            singleTask 
            如果应用没有该Activity   则直接创建

 

            如果发现该应用有Activity   那么会删除配置了该模式的Activity   栈上面的所有Activity   而且不会再次创建本身

 

                    

            -->

        <!-- 
        singleInstance
        就是配置了该模式的Activity   如果没有创建就自己创建一个容器并把Activity   放进去   "并把自己的容器提前"
        如果Activity   已经存在了  再次启动Activity   "把自己的容器提前了"
        如果一个应用里面 BAActivity   配置了该模式  此时BActivity   已经启动 此时BActivity   所在的容器提到最前 
        此时如果有新的Activity   被启动  其他的容器会提前 并在最前面的容器里添加该新的Activity   
         -->
        <activity
            android:name="com.taskstack.BActivity"
            android:label="@string/app_name"
            android:launchMode="singleInstance" >

        </activity>

android任务栈简单了解

1. android任务栈又称为Task,它是一个栈结构,具有后进先出的特性,用于存放我们的Activity组件。 

2. 我们每次打开一个新的Activity或者退出当前Activity都会在一个称为任务栈的结构中添加或者减少一个Activity组件,因此一个任务栈包含了一个activity的集合, android系统可以通过Task有序地管理每个activity,并决定哪个Activity与用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。 
3. 在我们退出应用程序时,必须把所有的任务栈中所有的activity清除出栈时,任务栈才会被销毁。当然任务栈也可以移动到后台, 并且保留了每一个activity的状态. 可以有序的给用户列出它们的任务, 同时也不会丢失Activity的状态信息。 
4. 需要注意的是,一个App中可能不止一个任务栈,某些特殊情况下,单独一个Actvity可以独享一个任务栈。还有一点就是一个Task中的Actvity可以来自不同的App,同一个App的Activity也可能不在一个Task中。

  嗯,目前android任务栈的概念我们就大概了解到这。下面我们主要还是来聊聊android的4种启动模式。

Activity的启动模式

为什么需要Activity的启动模式?

  我们在开发项目的过程中,一般都需要在本应用中多个Activity组件之间的跳转,也可能需要在本应用中打开其它应用的可复用的Activity。如我们可能需要跳转到原来某个Activity实例,此时我们更希望这个Activity可以被重用而不是创建一个新的 Activity,但根据Android系统的默认行为,确实每次都会为我们创建一个新的Activity并添加到Task中,这样android系统是不是很傻?还有一点就是在我们每开启一次页面加入到任务栈Task中后,一个Activity的数据和信息状态都将会被保留,这样会造成数据冗余, 重复数据太多, 最终还可能导致内存溢出的问题(OOM)。为了解决这些问题,android系统提供了一套Activity的启动模式来修改系统Activity的默认启动行为。目前启动模式有四种,分别是standard,singleTop,singTask和singleInstance,接下来我们将分别介绍这四种模式。

Activity的4种启动模式

  • Standard 模式

  又称为标准模式,也是系统的默认模式(可以不指定),在这样模式下,每启动一个Activity都会重新创建一个Activity的新实例,并且将其加入任务栈中,而且完全不会去考虑这个实例是否已存在。我们通过图解来更清晰地了解Standard模式: 

 
  通过上图,我们可以发现,这个过程中,在standard模式下启动了三次MainActivity后,都生成了不同的新实例,并添加到同一个任务栈中。这个时候Activity的onCreate、onStart、onResume方法都会被调用。

  • singleTop 模式

  又称栈顶复用模式,顾名思义,在这种模式下,如果有新的Activity已经存在任务栈的栈顶,那么此Activity就不会被重新创建新实例,而是复用已存在任务栈栈顶的Activity。这里重点是位于栈顶,才会被复用,如果新的Activity的实例已存在但没有位于栈顶,那么新的Activity仍然会被重建。需要注意的是,Activity的onNewIntent方法会被调用,方法原型如下:

@Overrideprotected void onNewIntent(Intent intent) {    super.onNewIntent(intent);}

  通过此方法的参数,我们可以获取当前请求的相关信息,此时Activity的onCreate、onStart方法不会被调用,因为Activity并没有被重建。同理,我们通过图解来协助我们更清晰的理解singleTop模式: 

 
  从上图我们可以看出,当需要新创建的MainActivity位于栈顶时,MainActivity并没有重新创建。下面我们再来看看新创建的MainActivity没有位于栈顶的情况。 
 
  嗯,这就是singTop模式。这种模式通常比较适用于接收到消息后显示的界面,如qq接收到消息后弹出Activity界面,如果一次来10条消息,总不能一次弹10个Activity,是吧?再比如新闻客户端收到了100个推送,你每次点一下推送他都会进入某个activiy界面(显示新闻只用一个activity,只是内容不同而已),这时也比较适合使用singleTop模式。

  • singleTask 模式

   又称为栈内复用模式。这是一种单例模式,与singTop点类似,只不过singTop是检测栈顶元素是否有需要启动的Activity,而singTask则是检测整个栈中是否存在当前需要启动的Activity,如果存在就直接将该Activity置于栈顶,并将该Activity以上的Activity都从任务栈中移出销毁,同时也会回调onNewIntent方法。情况如下图: 

 
   从图中可以看出,当我们再次启动MainActivity时,由于MainActivity位于栈中,所以系统直接将其置于栈顶,并移除其上方的所有Activity。当然如果所需要的MainActivity不存在栈中,则会创建新的Activity并添加到栈中。singleTask 模式比较适合应用的主界面activity(频繁使用的主架构),可以用于主架构的activity,(如新闻,侧滑,应用主界面等)里面有好多fragment,一般不会被销毁,它可以跳转其它的activity 界面再回主架构界面,此时其他Activity就销毁了。当然singTask还有一些比较特殊的场景这个我们后面会一一通过情景代码分析。

  • singleInstance 模式

  在singleInstance模式下,该Activity在整个android系统内存中有且只有一个实例,而且该实例单独尊享一个Task。换句话说,A应用需要启动的MainActivity 是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A单独在这个新的任务栈中,如果此时B应用也要激活MainActivity,由于栈内复用的特性,则不会重新创建,而是两个应用共享一个Activity的实例。如下图所示: 

 
  从图中我们可以看到最终AB应用都共享一个singleInstance模式的MainActivity,也没有去重新创建。到此Activity的四种启动模式我们都介绍完了,下面我们接着来聊聊怎么使用启动模式。

Activity启动模式的使用方式

  前面我们说了那么多,那么我们该如何给Activity指定启动模式呢?事实上共有如下两种方式: 

1.通过AndroidMenifest.xml文件为Activity指定启动模式,代码如下:

<activity android:name=".ActivityC"android:launchMode="singleTask" />

2.通过在Intent中设置标志位(addFlags方法)来为Activity指定启动模式,示例代码如下:

Intent intent = new Intent();intent.setClass(ActivityB.this,ActivityA.class);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);

那么标志位是是什么呢?接下来我们就来了解一些常用的标志位

Intent Flag 启动模式

  这里我们主要介绍一下一些常用的Activity的Flag,因为Activity的Flag比较多,我们知道一些常用的就够了,遇到比较特殊的还是查查官网文档吧。

  • Intent.FLAG_ACTIVITY_NEW_TASK

该标志位表示使用一个新的Task来启动一个Activity,相当于在清单文件中给Activity指定“singleTask”启动模式。通常我们在Service启动Activity时,由于Service中并没有Activity任务栈,所以必须使用该Flag来创建一个新的Task。我们来重现一下这个错误,创建一个Service服务,并在onCreate方法中启动Activity,代码如下:

public class ServiceT extends Service {    @Nullable    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onCreate() {        super.onCreate();        Intent i =new Intent(getApplicationContext(),ActivityD.class);        startActivity(i);    }}

启动应用并启动Service服务,后报错如下: 

从异常信息我们可以看出,提示我们添加Intent.FLAG_ACTIVITY_NEW_TASK标志位,所以我们代码必须改成如下:

public class ServiceT extends Service {    @Nullable    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onCreate() {        super.onCreate();        Intent i =new Intent(getApplicationContext(),ActivityD.class);        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        startActivity(i);    }}
  • Intent.FLAG_ACTIVITY_SINGLE_TOP 

      该标志位表示使用singleTop模式来启动一个Activity,与在清单文件指定android:launchMode="singleTop"效果相同。

  • Intent.FLAG_ACTIVITY_CLEAR_TOP 

    
  该标志位表示使用singleTask模式来启动一个Activity,与在清单文件指定android:launchMode="singleTask"效果相同。

  • Intent.FLAG_ACTIVITY_NO_HISTORY 

      使用该模式来启动Activity,当该Activity启动其他Activity后,该Activity就被销毁了,不会保留在任务栈中。如A-B,B中以这种模式启动C,C再启动D,则任务栈只有ABD。

  • Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 

      使用该标识位启动的Activity不添加到最近应用列表,也即我们从最近应用里面查看不到我们启动的这个activity。与属性android:excludeFromRecents="true"效果相同。

启动模式中singleTask的特殊情景

  前面我们在分析singleTask模式时,提到过singleTask模式有些比较特殊的场景,现在我们就来了解了解它们。 

特殊情景一:现在我们假设有如下两个Task栈,分别为前台任务栈和后台任务栈 
 
  从图中我们看出前台任务栈分别为AB两个Activity,后台任务栈分别为CD两个任务栈,而且其启动模式均为singleTask,此时我们先启动CD,然后再启动AB,再有B启动D,此时后台任务栈便会被切换到前台,而且这个时候整个后退列表就变成了ABCD,请注意我们这里强调的是后退列表,而非栈合并。因此当用户点击back键时,列表中的Activity会依次按DCBA顺序出栈,如下图所示: 
  这里我们通过两个应用ActivityTask和ActivityTask2来测试重现这个现象。因为两个是不同的应用所以启动时所在的栈也是不同。我们先启动ActivityTask2的应用,其ActivityC和ActivityD都是singleTask模式,然后再启动应用ActivityTask,此时ActivityC和ActivityD所在任务栈会被退居后台,而打开的ActivityA和ActivityB会在前台,而且都是默认模式。我们通过 adb shell dumpsys activity activities 命令查看此时栈的情况: 
  我们可以看到由两个栈,分别为id=222且栈名为“com.cmcm.activitytask”的任务栈其包含ActivityA和ActivityB(下面简称AB,栈名一般默认和包名相同),另外一个任务栈,id=221,栈名为“com.cmcm.activitytask2”,其包含ActivityC和ActivityD(下面检测CD)。现在我们通过ActivityB去启动ActivityD,然后按back键回退。B调用D代码如下:

import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.Button;/** * Created by zejian * Time 16/7/23. * Description: */public class ActivityB extends Activity {    private Button btn;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_b);        btn= (Button) findViewById(R.id.main);        btn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Intent intent = new Intent(Intent.ACTION_MAIN);                intent.addCategory(Intent.CATEGORY_LAUNCHER);                ComponentName cn = new ComponentName("com.cmcm.activitytask2", "com.cmcm.activitytask2.ActivityD");                intent.setComponent(cn);                startActivity(intent);            }        });    }}

运行结果如下: 

  我们可以看到包含CD的任务栈被提前的,虽然CD隔开了,但是我们从id和栈名可以发现他们是同一个栈,而AB所在的栈则在CD所在栈的后面,所以此时我们按back回退时,退出顺序是这样的D->C->B->A,动态图如下: 
 
  到这里我们就应该更加清晰的了解情景一的现象了。了解这点有什么用呢,这可以使用我们更好地去管理我们的任务栈,而不会导致栈混乱是进入一些用户本来就不需要界面,影响用户体验。

特殊情景二: 

  如果上面B不是请求启动D而是请求启动C,那么又会是什么情况呢?其实这个时候任务栈退出列表变成C->B->A,其实原因很简单,singleTask模式的ActivityC切换到栈顶时会导致在他之上的栈内的Activity出栈。同样我们还是使用上面的代码,把B启动D改为B启动C,那么此时B未启动C时任务栈的情况如下: 
  我们仍然可以看到两个任务栈,分别为id=242,栈名“com.cmcm.activitytask”的Task,包含ActivityA和ActivityB;id=241,栈名“com.cmcm.activitytask2”的Task,包含ActivityC和ActivityD。此时我们通过B启动C后栈的情况变成如下情况 
因此,栈的退出列表就变成了C->B->A了,如下图所示: 
动态图如下:

 

  到此我们对SingleTask模式又有了更深入的理解,但是我们发现上面的例子使用的是两个应用,所以才会有不同的任务栈,那么我们能不能在一个应用中存在多个不同的任务栈呢(暂时不考虑singleInstance 模式)?答案当然是肯定的啦,这就需要通过taskAffinity属性来设置不同的任务栈名称,不过这点将放在下篇来记录,本篇就先到这里告一段落哈。

转载地址:https://liwangjiang.blog.csdn.net/article/details/79683208 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Uri统一资源定位符
下一篇:Android四大组件Activity生命周期使用Activity的操作

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年04月13日 22时14分57秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章