使用dumpsys命令学Activity启动模式

来源:互联网 发布:淘宝部分店铺无法使用 编辑:程序博客网 时间:2024/04/30 16:36

使用dumpsys命令学Activity启动模式.

引言

我们知道,在默认情况下,我们在创建多个Activity的时候,系统会创建多个实例并放在任务栈中,当切换Activity时,会多次启动同一个Activity,重复创建多个实例,是非常浪费资源的不明智的行为.启动模式则是一种好的解决方案.

启动模式种类

  1. standard:标准模式,即默认模式.一个任务栈可有多个实例,每个实例可以属于不同任务栈.每启动一个Activity就重新创建一个新的实例,不管实例是否已经存在.谁启动了这个Activity,这个Activity就会运行在启动它的哪个Activity所在的栈中.若目前有四个Activity,ABCD,现在新建D,则变为ABCDD.
  2. singleTop:栈顶复用该模式.ABCD新建D->ABCD.
  3. singleTask:栈内复用模式.当一个具有singleTask模式的Activity启动后,系统首先寻找是否存在A想要的任务栈,如果不存在,就创建一个任务栈,创建activity实例并压入栈中;如果存在想要的任务栈,就看任务栈里是否存在此activity的实例,存在的话,就把A调到栈顶(并调用onNewIntent方法),不存在就创建A实例并压入栈中.总之,这个模式的最终结果为,activity在其想要的任务栈的栈顶(事实上,并不是将其位置移动,而是将其之上的activity全部出战,实现clearTop效果).
  4. singleInstance:加强的singleTask模式.具有此种启动模式的activity启动后,系统为它创建一个新的任务栈,然后A独自在这个新的 任务栈中,且栈内复用不会再创建新的activity.

启动模式操作

怎样给Activity指定启动模式?
1.第一种方法是在配置清单中设置.
这里写图片描述
2.第二种方法是在Intent中设置标志位(flags).
这里写图片描述
两种方法比较,后一种优先级高;后一种无法指定singleInstance模式.

启动模式实践方法

最好的学习方法是实际操作.我们用adb shell来真切的感受下.这里以standard模式为例.
首先添加一个button控件,点击则从当前Activity跳转的新的当前Activity实例,

代码:

public class MainActivity extends Activity {    private static final String TAG = "MainActivity";    private Context mContext;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        this.mContext = this;        setContentView(R.layout.activity_main);        Button btn_jump = (Button) findViewById(R.id.btn_jump);        btn_jump.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Intent intent = new Intent();                intent.setClass(MainActivity.this, MainActivity.class);//跳转到新的当前类型的activity                intent.putExtra("time;", System.currentTimeMillis());                startActivity(intent);            }        });    }}

启动app.连续点击五次跳转按钮.

命令:
adb devices//显示设备列表
adb -s xxx shell//连接指定设备并进入shell环境
dumpsys activity activities//查看activity信息

显示的内容很多.我们只关注必要信息,其他以省略号省略

generic_x86_64:/ $ dumpsys activity activitiesACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)Display #0 (activities from top to bottom):  Stack #1:  ...    * TaskRecord{5cc63d3 #32 A=com.example.libin.activitytest U=0 StackId=1 sz=6}   ...    Running activities (most recent first):      TaskRecord{5cc63d3 #32 A=com.example.libin.activitytest U=0 StackId=1 sz=6}        Run #5: ActivityRecord{e4f15da u0 com.example.libin.activitytest/.MainActivity t32}        Run #4: ActivityRecord{46de964 u0 com.example.libin.activitytest/.MainActivity t32}        Run #3: ActivityRecord{c35ee1e u0 com.example.libin.activitytest/.MainActivity t32}        Run #2: ActivityRecord{8bfce88 u0 com.example.libin.activitytest/.MainActivity t32}        Run #1: ActivityRecord{9204122 u0 com.example.libin.activitytest/.MainActivity t32}        Run #0: ActivityRecord{caf14e5 u0 com.example.libin.activitytest/.MainActivity t32}        ...  Stack #0:  ...    * TaskRecord{1bef863 #22 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}      ...    Task id #31    ...    * TaskRecord{a43f609 #31 A=com.android.systemui U=0 StackId=0 sz=1}      ...    Running activities (most recent first):      TaskRecord{1bef863 #22 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}        Run #1: ActivityRecord{775727d u0 com.android.launcher3/.Launcher t22}      TaskRecord{a43f609 #31 A=com.android.systemui U=0 StackId=0 sz=1}        Run #0: ActivityRecord{f0f0003 u0 com.android.systemui/.recents.RecentsActivity t31}    ......

我们只需关注其中的几行,
其中,每个TaskRecord可以看做是一个任务栈.

可以看出,系统给出了包括launcher(桌面)的任务栈和我们自己的应用的任务栈.我们有六个重复的Activity.这是因为在standard模式下,每次启动Activity都会创建一个新的实例放入任务栈中.

如果是栈内复用模式(SingleTask)呢?
修改配置清单

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"          package="com.example.libin.activitytest">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity            android:name=".MainActivity"            android:launchMode="singleTask">//修改为栈内复用模式            <intent-filter>                <action android:name="android.intent.action.MAIN"/>                <category android:name="android.intent.category.LAUNCHER"/>            </intent-filter>        </activity>    </application></manifest>

重复上述步骤,注意连点五次跳转,,输出为:

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)Display #0 (activities from top to bottom):  Stack #1:  ...    Task id #34    ...    * TaskRecord{7e6eec0 #34 A=com.example.libin.activitytest U=0 StackId=1 sz=1}      ...    Running activities (most recent first):      TaskRecord{7e6eec0 #34 A=com.example.libin.activitytest U=0 StackId=1 sz=1}        Run #0: ActivityRecord{b611c37 u0 com.example.libin.activitytest/.MainActivity t34}    ...  Stack #0:  ...      Task id #33      ...    * TaskRecord{85ff59f #33 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}    ...    Running activities (most recent first):      TaskRecord{85ff59f #33 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}        Run #0: ActivityRecord{f934282 u0 com.android.launcher3/.Launcher t33}

与上文比对,发现在我们的任务栈中,activity只存在一个实例.验证了singleTask模式.

singleTask的复杂情况

几种模式都很好验证,但是singTask模式有更复杂的情况.

这里给出一个新的配置参数:taskAffinity.
taskAffinity暂时即可以理解为任务栈.

试想,现有FirstActivity,SecondActivity,ThirdActivity,(分别简称为A,B,C),分别为standard,singleTask,singleTask模式,现在从A跳转到B,再从B跳转到C,再从C跳转到A,载跳转到B,现在按返回键,返回到哪里?

解答:
1. 如果启动模式为standard,那么其taskAffinity则继承自Application的taskAffinity,默认为包名
2. 从A跳转到B,由于B是singleTask模式,所以单独创建其想要的任务栈即com.test.task1.入栈.
3. 从B跳转到C,C是singleTask模式,启动时发现已经存在想要的com.test.task1,于是直接入栈.此时栈结构为A和BC,C在栈顶.
4. 从C跳转到A,A为standard模式,会在启动它的任务栈中创建实例,也就是说现在的栈结构为A和BCA,A在栈顶.
5. 再从A跳转到B,B为singleTask模式,由于已经存在于com.test.task1中,要到达栈顶,这就意味着其上方的CA需要出栈.出栈去哪里了?去了后台任务栈.
6. B到达栈顶,此时按返回键.会到达哪里?B在栈顶,但也是所在栈的栈底.B按返回键之后,会回到后台任务栈的栈A.

实验结果:
启动A:

    Running activities (most recent first):      TaskRecord{a8502c #40 A=com.example.libin.activitytest U=0 StackId=1 sz=1}        Run #0: ActivityRecord{8caeffc u0 com.example.libin.activitytest/.FirstActivity t40}

A->B:

    Running activities (most recent first):      TaskRecord{c896c92 #41 A=com.test.task1 U=0 StackId=1 sz=1}        Run #1: ActivityRecord{a604318 u0 com.example.libin.activitytest/.SecondActivity t41}      TaskRecord{a8502c #40 A=com.example.libin.activitytest U=0 StackId=1 sz=1}        Run #0: ActivityRecord{8caeffc u0 com.example.libin.activitytest/.FirstActivity t40}

B->C

    Running activities (most recent first):      TaskRecord{c896c92 #41 A=com.test.task1 U=0 StackId=1 sz=2}        Run #2: ActivityRecord{3332319 u0 com.example.libin.activitytest/.ThirdActivity t41}        Run #1: ActivityRecord{a604318 u0 com.example.libin.activitytest/.SecondActivity t41}      TaskRecord{a8502c #40 A=com.example.libin.activitytest U=0 StackId=1 sz=1}        Run #0: ActivityRecord{8caeffc u0 com.example.libin.activitytest/.FirstActivity t40}

C->A

    Running activities (most recent first):      TaskRecord{c896c92 #41 A=com.test.task1 U=0 StackId=1 sz=3}        Run #3: ActivityRecord{d421e42 u0 com.example.libin.activitytest/.FirstActivity t41}        Run #2: ActivityRecord{3332319 u0 com.example.libin.activitytest/.ThirdActivity t41}        Run #1: ActivityRecord{a604318 u0 com.example.libin.activitytest/.SecondActivity t41}      TaskRecord{a8502c #40 A=com.example.libin.activitytest U=0 StackId=1 sz=1}        Run #0: ActivityRecord{8caeffc u0 com.example.libin.activitytest/.FirstActivity t40}

A->B:

    Running activities (most recent first):      TaskRecord{c896c92 #41 A=com.test.task1 U=0 StackId=1 sz=1}        Run #1: ActivityRecord{a604318 u0 com.example.libin.activitytest/.SecondActivity t41}      TaskRecord{a8502c #40 A=com.example.libin.activitytest U=0 StackId=1 sz=1}        Run #0: ActivityRecord{8caeffc u0 com.example.libin.activitytest/.FirstActivity t40}

B->back:

    Running activities (most recent first):      TaskRecord{b1b4aa4 #43 A=com.example.libin.activitytest U=0 StackId=1 sz=1}        Run #0: ActivityRecord{bce10d9 u0 com.example.libin.activitytest/.FirstActivity t43}

通过比对序列号可以发现,此时显示的A并不是最开始启动的A,而是后台切换回来的A.

Intent Flags

本节转载自链接:http://blog.csdn.net/vipzjyno1/article/details/25463457

Flags: 表示Intent的标志位,常用于Activity的场景中,它和Activity的启动模式有着密切的联系。

下面列举的是和本文主题相关的Flags属性:

Intent.FLAG_ACTIVITY_NEW_TASK (默认)

默认的跳转类型,它会重新创建一个新的Activity,不过与这种情况,比如说Task1中有A,B,C三个Activity,此时在C中启动D的话,如果在AndroidManifest.xml文件中给D添加了Affinity的值和Task中的不一样的话,则会在新标记的Affinity所存在的Task中压入这个Activity。如果是默认的或者指定的Affinity和Task一样的话,就和标准模式一样了启动一个新的Activity.

FLAG_ACTIVITY_SINGLE_TOP

这个FLAG就相当于启动模式中的singletop,例如:原来栈中结构是A B C D,在D中启动D,栈中的情况还是A,B,C,D。

FLAG_ACTIVITY_CLEAR_TOP

这个FLAG就相当于启动模式中的SingleTask,这种FLAG启动的Activity会把要启动的Activity之上的Activity全部弹出栈空间。例如:原来栈中的结构是A B C D ,从D中跳转到B,栈中的结构就变为了A B了。(这个方法可以用来关闭多个Activity,之后的一篇博文里面会提到)

FLAG_ACTIVITY_BROUGHT_TO_FRONT

这个网上很多人是这样写的。如果activity在task存在,拿到最顶端,不会启动新的Activity。这个有可能会误导大家! 他这个FLAG其实是这个意思!比如说我现在有A,在A中启动B,此时在A中Intent中加上这个标记。此时B就是以FLAG_ACTIVITY_BROUGHT_TO_FRONT方式启动,此时在B中再启动C,D(正常启动C,D),如果这个时候在D中再启动B,这个时候最后的栈的情况是 A,C,D,B。如果在A,B,C,D正常启动的话,不管B有没有用FLAG_ACTIVITY_BROUGHT_TO_FRONT启动,此时在D中启动B的话,还是会变成A,C,D,B的。

FLAG_ACTIVITY_NO_USER_ACTION

onUserLeaveHint()作为activity周期的一部分,它在activity因为用户要跳转到别的activity而要退到background时使用。比如,在用户按下Home键,它将被调用。比如有电话进来(不属于用户的选择),它就不会被调用。
那么系统如何区分让当前activity退到background时使用是用户的选择?

它是根据促使当前activity退到background的那个新启动的Activity的Intent里是否有FLAG_ACTIVITY_NO_USER_ACTION来确定的。

注意:调用finish()使该activity销毁时不会调用该函数

FLAG_ACTIVITY_NO_HISTORY

意思就是说用这个FLAG启动的Activity,一旦退出,它不会存在于栈中,比方说!原来是A,B,C这个时候再C中以这个FLAG启动D的,D再启动E,这个时候栈中情况为A,B,C,E。
参考资料:
《Android开发艺术探索》 博主链接:http://blog.csdn.net/singwhatiwanna?viewmode=contents
intent flags转载自: http://blog.csdn.net/vipzjyno1/article/details/25463457

0 0