android中Activity的4种启动模式

最近在改写(优化)安卓程序,在此记录下开发安卓程序遇到的问题 及解决办法 或 查阅的资料。

 

一个实例让你彻底明白Activity的4种启动模式

首先,我们得有一个概念,就是启动的Activity都是放在相应的任务栈中。按Back键的时候Activity会从任务栈中返回,当任务栈为空时系统就会回收这个任务栈。

那么我们为什么需要这4中启动模式呢?我们新建Activity的并在Androidmanifest.xml文件中注册的时候,默认的就是standard模式,如果你在这个Activity中一直通过startActivity来启动这个Activity,那么任务栈中就会有许多该Activity,你要多次按返回键才能返回到launcher页面。这样的体验简直糟糕啊。

模式1:standard 

标准模式,也是Activity默认的启动方式。这个在Androidmanifest.xml中的Activity里声明,如下:

<activity
            android:name="luanchmode.ActivityB"
            android:launchMode="standard" />

以这种方式启动的Activity每次都会创建一个新的实例,不管这个实例是否已经存在。 
比如在 Activity A 中启动 A,那么任务栈中会有2个A。

例子1:ActivityA 中以standard启动 ActivityA,代码如下:

manefest.xml:

<activity android:name="luanchmode.ActivityA"
            android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

ActivityA.java:

public class ActivityA extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        Log.e("xxx", "ActivityA, onCreate");
        super.onCreate(savedInstanceState);

        TextView tv = new TextView(this);
        tv.setTextSize(20);
        tv.setText("This is activityA");
        tv.setGravity(Gravity.CENTER);
        tv.setBackgroundColor(Color.parseColor("#999999"));

        setContentView(tv, new ViewGroup.LayoutParams(200, 100));

        tv.setFocusable(true);
        tv.requestFocus();


        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("xxx","onclick A");
                Intent intent = new Intent(ActivityA.this, ActivityA.class);
                startActivity(intent);
            }
        });

    }

    @Override
    protected void onNewIntent(Intent intent) {
        Log.e("xxx", "ActivityA, onNewIntent");
        super.onNewIntent(intent);
    }
}

log如下:

08-02 19:33:45.720 11791-11791/com.example.aidlclient E/xxx: ActivityA, onCreate
08-02 19:33:59.192 11791-11791/com.example.aidlclient E/xxx: onclick A
08-02 19:33:59.207 11791-11791/com.example.aidlclient E/xxx: ActivityA, onCreate

可以看到 点击跳转到 ActivityA 的时候,任然会执行 onCreate方法。 
通过指令:adb shell dumpsys activity 查看任务栈:

  Running activities (most recent first):
    TaskRecord{2470eca6 #1587 A=com.example.aidlclient U=0 sz=2}
      Run #1: ActivityRecord{3456102c u0 com.example.aidlclient/luanchmode.ActivityA t1587}
      Run #0: ActivityRecord{352831f7 u0 com.example.aidlclient/luanchmode.ActivityA t1587}

可以看出任务栈中有2个ActivityA 。

模式2:singleTop 栈顶复用模式

在这种模式下启动的Activity,如果该Activity已经位于栈顶,那么就不会去创建新的实例,调用该Activity的onNewIntent方法。如果不在栈顶还是会新建一个Activity实例。

例子: A 启动B,B的启动模式为singleTop ,然后 B 再启动 B;

AndroidManifest.xml如下:

<activity android:name="luanchmode.ActivityA"
            android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="luanchmode.ActivityB"
            android:launchMode="singleTop" />
        <activity

AcitivityA 和 ActivityB 中的部分代码如下:

tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ActivityA.this, ActivityB.class);
                startActivity(intent);
            }
        });
 tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ActivityB.this, ActivityB.class);
//                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                startActivity(intent);
            }
        });

log如下:

08-03 09:13:01.841 26939-26939/com.example.aidlclient E/xxx: ActivityA, onCreate
08-03 09:18:08.513 26939-26939/com.example.aidlclient E/xxx: ActivityB, onCreate
08-03 09:18:09.983 26939-26939/com.example.aidlclient E/xxx: ActivityB, onNewIntent

可以看出从A跳转到B时,由于栈中没有B,所以会新建B的实例并放入栈中;从B跳到B中,由于栈顶以经是B了,所以不会新建实例,而是会调用B的onNewIntent方法。

我们看看任务栈中是不是只有A和B ?

 Running activities (most recent first):
    TaskRecord{351e6f43 #1596 A=com.example.aidlclient U=0 sz=2}
    Run #1: ActivityRecord{2925d326 u0 com.example.aidlclient/luanchmode.ActivityB t1596}
    Run #0: ActivityRecord{1e64c90 u0 com.example.aidlclient/luanchmode.ActivityA t1596}

可以看到任务栈中只有A和B,也可以看出如果如果B在栈顶就不会创建新的实例。接下来我们看看如果B不是栈顶,那又会如何?

接下来的例子是这样的 A 启动 B, B 启动 C, C 再启动 B: 
代码很简单就不贴A和B的了,贴下C的。

tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ActivityC.this, ActivityB.class);
//                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                startActivity(intent);
            }
        });

log如下:

08-03 09:36:35.259 17108-17108/com.example.aidlclient E/xxx: ActivityA, onCreate
08-03 09:36:39.482 17108-17108/com.example.aidlclient E/xxx: ActivityB, onCreate
08-03 09:36:40.979 17108-17108/com.example.aidlclient E/xxx: ActivityC onCreate
08-03 09:36:42.171 17108-17108/com.example.aidlclient E/xxx: ActivityB, onCreate

可以看出,C再次启动B的时候B会创建新的实例。我们查看下任务栈看看是什么样的,任务栈中应该是 A->B->C->B.

 Running activities (most recent first):
      TaskRecord{1d40b112 #1602 A=com.example.aidlclient U=0 sz=4}
        Run #3: ActivityRecord{3482a81a u0 com.example.aidlclient/luanchmode.ActivityB t1602}
        Run #2: ActivityRecord{a101309 u0 com.example.aidlclient/luanchmode.ActivityC t1602}
        Run #1: ActivityRecord{3575d5f8 u0 com.example.aidlclient/luanchmode.ActivityB t1602}
        Run #0: ActivityRecord{31f704f3 u0 com.example.aidlclient/luanchmode.ActivityA t1602}

以上2个例子验证了,以singleTop启动的Activity,如果栈顶是该Activity,那么不会创建新的实例,如果该Activity不在栈顶,要启动这个Activity还是会创建新的实例。

模式3:singleTask 站内复用模式

在这种模式下,如果Activity存在于栈中,那么启动这个Activity不会创建新的实例,而是会调用onNewIntent(),并且将该Activity上面的所有Activity移出栈;当以singleTask 启动一个Activity的时候,首先去判断是否要为该Activity去创建一个任务栈?怎么判断的我们下面再讲,如果需要的话,那么就会创建一个任务栈,并且将该Activity放入栈中;如果不需要的话,直接将该Activity放入当前的任务栈中。

现在我们来讲怎么判断是否需要创建任务栈?任务栈的创建跟taskAffinity的属性相关,每个Activity都有taskAffinity属性,这个属性指出了它希望进入的Task。如果一个Activity没有显式的指明该Activity的taskAffinity,那么它的这个属性就等于Application指明的taskAffinity,如果Application也没有指明,那么该taskAffinity的值就等于包名。

在singleTask 模式下,如果你在要启动的Activity中设置taskAffinity属性,且该属性的值不等于包名,那么就需要创建任务栈,否则不需要创建。taskAffinity的属性设置如下:
 

<activity
            android:name="luanchmode.ActivityB"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.aidlclient.test" />

而我的应用的包名是:package=”com.example.aidlclient”>

例子1:A中启动B,B中启动C,然后C启动D,D再启动B,其中A和C、D是standard模式,B是singleTask模式。

manifest.xml如下:

<activity
            android:name="luanchmode.ActivityA"
            android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="luanchmode.ActivityB"
            android:launchMode="singleTask" />
        <activity android:name="luanchmode.ActivityC" />
        <activity android:name="luanchmode.ActivityD" />

还是先看log:

08-03 10:40:33.896 14823-14823/? E/xxx: ActivityA, onCreate
08-03 10:40:42.777 14823-14823/? E/xxx: ActivityB, onCreate
08-03 10:40:44.169 14823-14823/? E/xxx: ActivityC onCreate
08-03 10:40:45.218 14823-14823/? E/xxx: ActivityD onCreate
08-03 10:40:46.696 14823-14823/? E/xxx: ActivityB, onNewIntent

A->B : 由于栈中没有B且未对B设置android:taskAffinity属性,所以直接创建B的实例并放在该栈中; 
B->C->D:创建C和D的实例并放到栈中 
D->B : 由于B已经存在栈中,所以直接将B移到栈顶,调用onNewIntent(),并且B上面的Activity全部出栈。

任务栈如下:

Running activities (most recent first):
      TaskRecord{33b61cae #1611 A=com.example.aidlclient U=0 sz=2}
        Run #1: ActivityRecord{2291853f u0 com.example.aidlclient/luanchmode.ActivityB t1611}
        Run #0: ActivityRecord{390ec8d6 u0 com.example.aidlclient/luanchmode.ActivityA t1611}

可以看到A和B都在同一个栈中,且从D跳转到B的时候,B上面的Activity全部出栈。

例子2:为B设置android:taskAffinity=”com.example.aidlclient.test”和包名不一样

manifest.xml如下:

<activity
            android:name="luanchmode.ActivityB"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.aidlclient.test"/>

log如下:

08-03 10:45:24.335 15566-15566/com.example.aidlclient E/xxx: ActivityA, onCreate
08-03 10:45:38.858 15566-15566/com.example.aidlclient E/xxx: ActivityB, onCreate
08-03 10:45:40.912 15566-15566/com.example.aidlclient E/xxx: ActivityC onCreate
08-03 10:45:42.694 15566-15566/com.example.aidlclient E/xxx: ActivityD onCreate
08-03 10:45:44.406 15566-15566/com.example.aidlclient E/xxx: ActivityB, onNewIntent

A->B:由于B不存在与任务栈中,所以会新建B的实例,至于有没有创建对应的栈,log中看不出,等下通过adb来查看。 
B->C->D:正常启动,创建C和D的实例并放入任务栈 
D->B:由于B存在于任务栈中,所以直接将B移到栈顶,调用onNewIntent(),并且B上面的Activity全部出栈。

利用 adb shell dumpsys activity查看任务栈情况:

先看 A->B->C->D:的任务栈详情:
 

Running activities (most recent first):
      TaskRecord{3874c557 #1618 A=com.example.aidlclient.test U=0 sz=3}
        Run #3: ActivityRecord{84f09e0 u0 com.example.aidlclient/luanchmode.ActivityD t1618}
        Run #2: ActivityRecord{255bda6b u0 com.example.aidlclient/luanchmode.ActivityC t1618}
        Run #1: ActivityRecord{2ac8c0a u0 com.example.aidlclient/luanchmode.ActivityB t1618}
      TaskRecord{1d0e98b2 #1617 A=com.example.aidlclient U=0 sz=1}
        Run #0: ActivityRecord{3863a8b u0 com.example.aidlclient/luanchmode.ActivityA t1617}

可以看出 B、C、D在一个任务栈,A在另外一个任务栈。

先看 A->B->C->D->B:的任务栈详情

 Running activities (most recent first):
      TaskRecord{3874c557 #1618 A=com.example.aidlclient.test U=0 sz=1}
        Run #1: ActivityRecord{2ac8c0a u0 com.example.aidlclient/luanchmode.ActivityB t1618}
      TaskRecord{1d0e98b2 #1617 A=com.example.aidlclient U=0 sz=1}
        Run #0: ActivityRecord{3863a8b u0 com.example.aidlclient/luanchmode.ActivityA t1617}

可以看出,从B->D后,会将B上面的Activity全部移除出栈。

 

模式4:singleInstance 

这种模式下,Activity只能单独的存在一个任务栈中,举个例子,就是说A以singleInstance 启动了B,那么会创建一个任务栈,并把B放到创建的任务栈中,该栈只能有B的存在。

例子1:A启动B,B启动C,其中A、C的启动模式为standard,D的启动模式为singleInstance 。
manifest.xml如下:

<activity
            android:name="luanchmode.ActivityA"
            android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="luanchmode.ActivityB"
            android:launchMode="singleInstance" />
        <activity android:name="luanchmode.ActivityC" />

log如下:

08-03 11:07:29.810 5490-5490/com.example.aidlclient E/xxx: ActivityA, onCreate
08-03 11:07:37.698 5490-5490/com.example.aidlclient E/xxx: ActivityB, onCreate
08-03 11:07:41.398 5490-5490/com.example.aidlclient E/xxx: ActivityC onCreate
08-03 11:08:37.934 5490-5490/com.example.aidlclient E/xxx: ActivityB, onNewIntent

A->B:由于不存在B需要的任务栈,所以先创建任务栈,然后将B压入栈 
B->C:正常启动,创建C的实例并入栈。 
C->B:不会创建B新的实例,而是调用onNewIntent()。

先看 A->B->C的任务栈详情:

Running activities (most recent first):
      TaskRecord{2d63becf #1621 A=com.example.aidlclient U=0 sz=2}
        Run #2: ActivityRecord{340fa242 u0 com.example.aidlclient/luanchmode.ActivityC t1621}
      TaskRecord{323683de #1622 A=com.example.aidlclient U=0 sz=1}
        Run #1: ActivityRecord{2aef961d u0 com.example.aidlclient/luanchmode.ActivityB t1622}
      TaskRecord{2d63becf #1621 A=com.example.aidlclient U=0 sz=2}
        Run #0: ActivityRecord{3486b42c u0 com.example.aidlclient/luanchmode.ActivityA t1621}

A->B 会新建一个任务栈然后创建B的实例并压入该栈;B->C 直接创建C的实例并压入之前A所在的栈。

Activity的4种启动模式就写到这里,下面会写Intent中设置flag,以及taskAffinity。


--------------------- 
原文:https://blog.csdn.net/zhujiangtaotaise/article/details/76619274 

 

在Android Studio 中 activity的四中启动模式

在进入启动模式学习前我们先了解一下:

1、一个应用程序一般都是由多个activity组成的。
2.任务栈(task stack)(别名back stack后退栈) 记录存放用户开启的activity的。

3、一个应用程序一被开启系统就给他分配一个任务栈,当所有的activity都退出的时候,任务栈就清空了。
4、任务栈的id是一个integer的数据类型 自增长的。
5、在android操作系统里面会存在多个任务栈,一个应用程序一个任务栈。
6、桌面应用和一般的应用程序是一样的,任务栈的行为也是一样。
7、默认情况下, 关闭掉一个应用程序,清空了这个应用程序的任务栈。应用程序的进程还会保留。

下面进入正题。。。

Activity的启动模式

  Android为了使我们能够打破默认的堆栈先进后出模式,提供了两个种方式:一种是在AndroidManifest.xml定义Activity时指定它的加载模式,另一种是在用Intent开启一个Activity时,在Intent中加入标志。如果两种方式都用了,则后者的优先级更高。
    两种方式的差别在于,前者在于描述自己,向别的Activity等声明你们如何来加载我;而后者则是动态的,指出我要求你(要启动的Activity)以某种启动模式启动。

  根据Activity的不同的启动模式,它在Task Stack(别名 BackStack 回退栈)中的状态是不一样的。Activity可以通过AndroidManifest.xml清单文件配置,在<Activity />节点中的android:launchMode属性设置。它有四个选项:

Activity的启动方式:

 ·standard 

 ·singleTop

 ·singleTask

 ·singleInstance

Android Studio 在实际开发中,根据需求为每个Activity指定恰当的启动方式。从而可以避免一些问题

设置activity的启动模式,只需要在AndroidManifest.xml里对应的<activity>标签设置Android:launchMode属性,例如:

        <activity
            android:name=".A1"
            android:launchMode="standard" />

standard 

标准启动模式,也是默认启动模式,如果不设置android:launchMode属性的话。standard模式下的Activity会依照启动的顺序压入Task Stack中。

  下图是standard模式下,Activity的压栈和回退操作示意图:

 

singleTop

顶部单例模式,这种Activity启动模式,启动一个Activity的时候如果发现Task Stack的栈顶已经存在这个Activity了,就不会去重新创建新的Activity对象,而是复用这个栈顶已经存在的Activity对象,避免同一个Activity被重复开启。

  下图是singleTop模式下,Activity的压栈和回退操作示意图:

singleTop的应用场景很多,一般适用于可以复用而又有多个开启渠道的Activity,避免当一个Activity已经开启并获得焦点后,再次重复开启。比如说浏览器书签页面, 避免栈顶的activity被重复的创建,解决用户体验问题。,就是一个singleTop模式的Activity。Android的浏览器是基于WebKit内核编写的,它是支持JavaScript脚本语言的,可以通过JavaScript脚本设置浏览器书签,这样如果存在多个页面存在保存书签的JavaScript脚本,就会导致书签页面被多次开启,所以书签页面被设置为singleTop模式,这样可以避免在保存多个书签的时候重复开启书签页面。 应用场景: 
 

singleTask

当Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task中共存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。

下图:

singleTask的的适用场景为一般程序的主页面,当回退到主页面的时候,清除Task Stack中,它之上的所有Activity,这样避免程序导航逻辑的混乱。比如Android系统的浏览器的主页面,就是singleTask模式的,上面提到,android下浏览器是Webkit内核的,它是由C语言编写的,而每次打开新的网页如果重新开启一个Activity,是非常耗费系统资源的(需要解析HTML、Script脚本),所以被设置为singleTask模式,这样在浏览器应用里,无论打开多少个页面,使用的都是同一个Activity。所以以后如果存在很耗费系统资源的Activity,可以考虑使用singleTask开启模式。
 

singleInstance

被标记为singleInstance启动模式的Activity,在启动的时候,会开启一个新的Task Stack,这个BackStack里只有一个Activity的实例存在,并且把这个Task Stack获得焦点。这是一种很极端的模式,它会导致整个设备的操作系统里,只会存在一个这个Activity示例,无论是从何处被启动的。

只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。这是一个极端的模式。

singleInstance一般适用于需要在系统中只存在一个实例的场景,比如Android系统的来电页面,多次来电均使用的是一个Activity。

 

 当然,在Android中,除了在AndroidManifest.xml清单文件中配置LauncherMode属性外,还可以在代码中设置启动模式。在组件中,启动一个Activity,需要用到startActivity()方法,其中传递一个Intent,可以使用Intent.setFlags(int flags)来设置新启动的Activity的启动模式,而通过代码设置Activity的启动模式的方式,优先级要高于在AndroidManifest.xml清单文件中的配置。 

  Intent.setFlag(int flags)方法传递的一个整形的数据,被Android系统设置为了常量:
 

FLAG_ACTIVITY_NEW_TASK:这个标识会使新启动的Activity独立创建一个Task。
FLAG_ACTIVITY_CLEAR_TOP:这个标识会使新启动的Activity检查是否存在于Task中,如果存在则清除其之上的Activity,使它获得焦点,并不重新实例化一个Activity,一般结合FLAG_ACTIVITY_NEW_TASK一起使用。
FLAG_ACTIVITY_SINGLE_TOP:等同于在LauncherMode属性设置为singleTop。


--------------------- 
原文:https://blog.csdn.net/itluochen/article/details/52599083

其他资源参考链接:

Activity状态转换和Activity栈

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值