Android之LaunchMode(启动模式)

Posted 破晓绝世

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android之LaunchMode(启动模式)相关的知识,希望对你有一定的参考价值。

android之LaunchMode(启动模式)

文章目录

Activity的4种启动模式

注意: Activity的启动模式将会影响到手机系统的任务栈, 所以想深刻的理解启动模式,一定要先了解怎么去看任务栈,其实很简单输入一条命令即可

 adb shell dumpsys activity activities|grep -E 'Stack|TaskRecord|Hist'

任务栈
任务栈首先是个栈,是一种先进后出的结构,一般显示在我们眼前的Activity就在栈顶,当我们按了back键之后,栈顶活动出站并销毁,这时屏幕会显示现在栈顶所显示的活动, 如果栈为空,这个任务栈将会被系统收回.

1.1 standard ------ 标准模式

这个是Activity的默认启动模式
标准模式就是在一个任务栈里,创建就入栈,销毁就出栈。将被启动的Activity加入到启动它的Activity所属的任务栈中。

==注:==如果用非Activity的Context(比如ApplicationContext)启动Activity,会报错,原因是该Context不存在任务栈。

1.2 singleTop

顾名思义:栈顶复用启动模式

当栈顶的Activity与即将要启动的Activity是同一个时,直接使用栈顶的Activity,因为当前栈顶的Activity正在显示在用户眼前,所以不会重新调用onCreate,onStart方法,但是会回调onPause和onResume方法(先执行onPause方法)。

在复用栈顶的Activity后,会回调该Activity的onNewIntent(Intent)方法。

方法名执行时机参数
onNewIntent(Intent)在复用Activity后执行,执行时机按在onPause和onResume之间该参数Intent代表启动该Activity的Intent

注: 该launcherModel仅仅是栈顶复用,如果重复启动的Activity在栈内,是不会复用的。

1.3 singleTask

栈内复用模式
这个比singleTop模式复杂一些

LunchMode是singleTask的Activity默认具有clearTop效果,会把栈内复用的Activity之上的Activity全部出栈

该LunchMode和SingleTop一样,栈内复用后,会回调onNewIntent(Intent intent)方法,只是该方法的调用时机略有不同

情况回调时机
复用的Activity在栈顶和singleTop一样,会执行该Activity的onPause,onResume方法。onNewIntent的回调时机在这两个方法之间
复用的Activity在栈内会使该Activity栈内之上的Activity出栈,回调该Activity的onStart和onResume方法。onNewIntent的回调时机在这两个方法之间
1.3.1 什么是Activity想要的任务栈

通过命令

adb shell dumpsys activity activities

可以看到任务栈有一个名字

这个名字就是项目的包名。
默认情况下每个Activity想要的任务栈就是任务栈名为它所属项目的包名的任务栈
Activity想要的任务栈可由字段taskAffinity在AndroidMainifest.xml中设定。
taskAffinity属性的设置格式:不能与项目包名一致,并且必须有包名分隔符 .

taskAffinity属性除了可以和singleTask配合使用之外还可以与allowTaskReparenting配合使用实现Activity两个任务栈内的迁移,这里就不细讲了,感兴趣的可以百度

1.4 singleInstance

单实例模式,可以理解为singleTask的加强版,但即将要启动的Activity的LauncherModel是singleInstance时,会重新给为它创建一个任务栈,将它入栈,后续将一直可以复用此Activity,直到栈被销毁。

LauncherModel的设定方式

2.1 通过Flag动态设定

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

常见的Flags有

Flags作用
FLAG_ACTIVITY_NEW_TASK设置此Flag不等价将LaunchModel设定为singleTask,仅仅表示将此Activity加入到TaskAffnity指定的任务栈中
FLAG_ACTIVITY_SINGLE_TOP将Activity的LaunchModel设定为singleTop
FLAG_ACTIVITY_CLEAR_TOP将栈中Activity之上的其他Activity全部出栈,singleTask默认具有该属性
FLAG_ACTIVITY_EXCLUDE_FROM_TASK该Activity的不会出现在后台任务的列表中,和在Androidmainfest.xml中设定android:excludeFromRecents="true"同一个意思

2.2 在AndroidMainfest文件中设定

android:launchMode="singleTask"

2.3 优劣比较

  1. Flags方式的优先级比在清单文件中的优先级高,两种方式同时设置时Flags的方式会生效
  2. Flags方式不能设置singleInstance
  3. 在AndroidMainfest文件中不能设定FLAG_ACTIVITY_CLEAR_TOP

Android-Activity启动模式(launchMode)

Activity启动模式是非常重要的一块内容,启动模式直接关系到用户的体验 和 性能的提升等

 

Activity启动模式分为四种:

  

  如果不配置:launchMode,默认就是:standard 标准的

  standard 标准的

  singleTop 独占顶端

  singleTask 单任务

  singleInstance 单实例

 


 

 

standard 标准的,特点是:启动一个Activity就进栈一个Activity,启动六个Activity就进栈六个Activity

 


 

singleTop 独占顶端,特点是NewActivity在顶端的时候,启动NewActivity会自动重用NewActivity,不会进栈

NewActivity设置为 独占顶端模式(singleTop) 的配置:

     <!-- 启动模式 实验的Activity singleTop -->
        <activity android:name=".launch_mode.NewActivity"
                  android:launchMode="singleTop"
                  />

 

NewActivity设置为 独占顶端模式(singleTop) 的效果图:

(当NewActivity在顶端,无论点击启动自己多少此都不会进栈 而是重用)

(当点击返回back键的时候,任务栈里面只有两个Activity的引用)

12-12 20:15:24.624 2347-2347/liudeli.activity D/launchMode: LoginActivity 任务栈ID: 457
12-12 20:15:40.820 2347-2347/liudeli.activity D/launchMode: NewActivity 任务栈ID: 457
12-12 20:15:44.081 2347-2347/liudeli.activity D/launchMode: onNewIntent NewActivity被重用了
12-12 20:15:47.190 2347-2347/liudeli.activity D/launchMode: onNewIntent NewActivity被重用了
12-12 20:15:48.611 2347-2347/liudeli.activity D/launchMode: onNewIntent NewActivity被重用了
12-12 20:15:50.180 2347-2347/liudeli.activity D/launchMode: onNewIntent NewActivity被重用了
12-12 20:15:50.535 2347-2347/liudeli.activity D/launchMode: onNewIntent NewActivity被重用了

 


 

 singleTask 单任务,特点是单任务,不会有两个NewActivity引用,NewActivity一旦进栈 就不会再次进栈了

例如:NewActivity设置了 singleTask启动模式:当启动过一次NewActivity,再次启动NewActivity的时候,会自动从栈底往上找,一旦找到NewActivity(一直杀到最顶端)

 

NewActivity设置为 独占顶端模式(singleTask) 的配置:

     <!-- 启动模式 实验的Activity singleTask -->
        <activity android:name=".launch_mode.NewActivity"
                  android:launchMode="singleTask"
                  />

 

 NewActivity设置为 独占顶端模式(singleTask) 的效果:

(当启动过一次NewActivity,启动三次LoginActivity 然后再次启动NewActivity的时候 会从栈底往上找 一旦找到 直接杀到栈顶 然后NewActivity会被重用

(当点击返回back键的时候,任务栈里面只有两个Activity引用)

12-12 20:44:34.646 2935-2935/? D/launchMode: LoginActivity 任务栈ID: 462
12-12 20:44:38.398 2935-2935/liudeli.activity D/launchMode: NewActivity 任务栈ID: 462 
12-12 20:44:40.501 2935-2935/liudeli.activity D/launchMode: LoginActivity 任务栈ID: 462 
12-12 20:44:41.919 2935-2935/liudeli.activity D/launchMode: LoginActivity 任务栈ID: 462
12-12 20:44:42.884 2935-2935/liudeli.activity D/launchMode: LoginActivity 任务栈ID: 462
12-12 20:44:43.752 2935-2935/liudeli.activity D/launchMode: LoginActivity 任务栈ID: 462
12-12 20:44:47.421 2935-2935/liudeli.activity D/launchMode: onNewIntent NewActivity被重用了 

 


 

 

singleInstance 单实例,特点就是:再次启动NewActivity的时候会重用,不会进栈

singleTop 和 singleInstance 对比:

  singleTop只有在顶端才会被重用

  singleInstance在任意端都会被重用,singleInstance还会单独开启一个任务栈

NewActivity设置为 独占顶端模式(singleInstance) 的配置:

     <!-- 启动模式 实验的Activity singleInstance -->
        <activity android:name=".launch_mode.NewActivity"
                  android:launchMode="singleInstance"
                  />

 

NewActivity设置为 独占顶端模式(singleInstance) 的效果:

(当启动过一次NewActivity,在启动LoginActivity,当再次启动NewActiviy的时候会断开(第一次启动NewActivity的引用),连接现在新的引用) 

(点击返回Back的时候,任务栈里面只有三个Activity的引用了)

12-12 21:19:12.011 3374-3374/liudeli.activity D/launchMode: LoginActivity 任务栈ID: 463
12-12 21:19:27.999 3374-3374/liudeli.activity D/launchMode: NewActivity 任务栈ID: 464
12-12 21:19:36.304 3374-3374/liudeli.activity D/launchMode: LoginActivity 任务栈ID: 463
12-12 21:19:41.347 3374-3374/liudeli.activity D/launchMode: onNewIntent NewActivity被重用了

 


 

 

测试四种启动模式的代码:---------------------

 

LoginActivity.java

package liudeli.activity.launch_mode;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import liudeli.activity.R;

public class LoginActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        /**
         *  每款应用程序启动后,会默认有一个任务栈(用于存放Activity的引用)
         *  任务栈有栈ID,随着应用程序的启动 栈ID是累加的
         */
        int taskId = getTaskId();
        Log.d("launchMode", "LoginActivity 任务栈ID: " + taskId);
    }

    /**
     * 启动 NewActivity
     * @param view
     */
    public void startNewActivity(View view) {
        startActivity(new Intent(this, NewActivity.class));
    }

    /**
     * 启动自己
     * @param view
     */
    public void startThis(View view) {
        startActivity(new Intent(this, LoginActivity.class));
    }
}

activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动新的Activity"
        android:onClick="startNewActivity"
        />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动自己"
        android:onClick="startThis"
        android:layout_alignParentRight="true"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LoginActivity 永远都是标准模式"
        android:layout_centerInParent="true"
        android:textSize="20sp"
        />

</RelativeLayout>

 

 

NewActivity.java

onNewIntent() 方法可以监听Activity是否被重用了
package liudeli.activity.launch_mode;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import liudeli.activity.R;

public class NewActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new);

        /**
         *  每款应用程序启动后,会默认有一个任务栈(用于存放Activity的引用)
         *  任务栈有栈ID,随着应用程序的启动 栈ID是累加的
         */
        int taskId = getTaskId();
        Log.d("launchMode", "NewActivity 任务栈ID: " + taskId);
    }

    /**
     * 启动登录Activity
     * @param view
     */
    public void startLoginActivity(View view) {
        startActivity(new Intent(this, LoginActivity.class));
    }

    /**
     * 启动自己
     * @param view
     */
    public void startThis(View view) {
        startActivity(new Intent(this, NewActivity.class));
    }

    /**
     * 当Activity被重用了就会调用此方法
     * @param intent
     */
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.d("launchMode", "onNewIntent NewActivity被重用了");
    }
}

activity_new.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动登录Activity"
        android:onClick="startLoginActivity"
        />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="启动自己"
        android:onClick="startThis"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="newActivity"
        android:layout_centerInParent="true"
        android:textSize="20sp"
        android:textColor="#f00"
        />

</RelativeLayout>

 

以上是关于Android之LaunchMode(启动模式)的主要内容,如果未能解决你的问题,请参考以下文章

Android基础之Activity 运行模式与回退栈

android launchmode 使用场景

全面解析Activity启动模式(LaunchMode)

Android-Activity启动模式(launchMode)

android launchmode singleinstance问题

Android启动模式之singleinstance的坑