Activity singleInstance/singleTop启动模式的理解

Android有四种启动模式,分别是standard,singleTop,singleTask,singleInstance。下面分别简单的介绍下这四种启动模式的作用。

standard

Android 默认的一种启动模式。不需要为activity设置launchMode。这种启动模式简单的来说就是当你startActivity的时候,他就创建一个。

singleTop

这种模式模式从字面意思就能看得出来,就是当前的activity处于栈顶的时候,当你startActivity当前的activity的时候,它不会创建新的activity,而是会复用之前的activity。举个例子,startActivity了一个ActivityA,ActivityA又startActivity了ActivityB,当在ActivityB再次startActivity一个ActivityB的时候,它不会创建一个新的ActivityB,而是复用之前的ActivityB。
这里需要注意的是,只有当前的activity处于栈顶的时候才管用。举个例子:startActivity了一个ActivityA,ActivityA又startActivity了ActivityB,ActivityB又startActivity了ActivityA,那么ActivityA还是会重新创建,而不是复用之前的ActivityA。

singleTask

单一任务。意思就是说当前的activity只有一个实例,无论在任何地方startActivity出来这个activity,它都只存在一个实例。并且,它会将在他之上的所有activity都销毁。通常这个activity都是用来作为MainActivity。因为主页只需要存在一个,然后回到主页的时候可以将所有的activity都销毁起到退出应用的作用。举个例子,startActivity了一个ActivityA,ActivityA的启动模式为singleTask,那么在ActivityA里startActivity了一个ActivityB,在ActivityB里startActivity了一个ActivityC。此时在当前的任务栈中的顺序是,ActivityA->ActivityB->ActivityC。然后在ActivityC里重新startActivity了一个ActivityA,此时ActivityA会将存在于它之上的所有activity都销毁。所以此时任务栈中就只剩下ActivityA了。

singleInstance

这个模式才是重点,也是比较容易入坑的一种启动模式。字面上理解为单一实例。它具备所有singleTask的特点,唯一不同的是,它是存在于另一个任务栈中。上面的三种模式都存在于同一个任务栈中,而这种模式则是存在于另一个任务栈中。

注意事项

以往的理解中,只要在`AndroidManifest.xml`中声明`singleTop`或者`singleInstance`,那么在使用`startActivity`或者`startActivityForResult`的时候,自动就会保证`singleInstance`的情况下只有一个对象,`singleTop`的情况下不会两个相同的`Activity`叠加在一起。

但是现实是让人崩溃的。

使用如下的代码:

package com.mobibrw.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private final static int SINGLE_INSTANCE = 1;
    private final static int SINGLE_TOP = 2;

    private void startSingleInstanceActivity() {
        final Intent intent = new Intent(this, SingleInstanceActivity.class);
        //intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivityForResult(intent, SINGLE_INSTANCE);
    }

    private void startSingleTopActivity() {
        final Intent intent = new Intent(this, SingleTopActivity.class);
        //intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivityForResult(intent, SINGLE_TOP);
    }

    private void startSingleInstanceWithFlagsActivity() {
        final Intent intent = new Intent(this, SingleInstanceActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivityForResult(intent, SINGLE_INSTANCE);
    }

    private void startSingleTopWithFlagsActivity() {
        final Intent intent = new Intent(this, SingleTopActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivityForResult(intent, SINGLE_TOP);
    }

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

        Button btnSingleTop = findViewById(R.id.SingleTop);
        btnSingleTop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startSingleTopActivity();
                startSingleTopActivity();
            }
        });
        Button btnSingleInstance = findViewById(R.id.SingleInstance);
        btnSingleInstance.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startSingleInstanceActivity();
                startSingleInstanceActivity();
            }
        });

        Button btnSingleTopWithFlags = findViewById(R.id.SingleTopWithFlags);
        btnSingleTopWithFlags.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startSingleTopWithFlagsActivity();
                startSingleTopWithFlagsActivity();
            }
        });
        Button btnSingleInstanceWithFlags = findViewById(R.id.SingleInstanceWithFlags);
        btnSingleInstanceWithFlags.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startSingleInstanceWithFlagsActivity();
                startSingleInstanceWithFlagsActivity();
            }
        });
    }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mobibrw.myapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".SingleInstanceActivity"
            android:launchMode="singleInstance"/>
        <activity
            android:name=".SingleTopActivity"
            android:launchMode="singleTop"/>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

在两次触发`startSingleTopActivity`/`startSingleInstanceActivity`的时候,出现了两个叠加的`Activity`,如下:

$ adb shell dumpsys activity | grep SingleTopActivity 
        Hist #2: ActivityRecord{6f4fc4 u0 com.mobibrw.myapplication/.SingleTopActivity t457}
          Intent { cmp=com.mobibrw.myapplication/.SingleTopActivity }
        Hist #1: ActivityRecord{4a4ec71 u0 com.mobibrw.myapplication/.SingleTopActivity t457}
          Intent { cmp=com.mobibrw.myapplication/.SingleTopActivity }
        Run #1: ActivityRecord{6f4fc4 u0 com.mobibrw.myapplication/.SingleTopActivity t457}
    mResumedActivity: ActivityRecord{6f4fc4 u0 com.mobibrw.myapplication/.SingleTopActivity t457}
  mFocusedActivity: ActivityRecord{6f4fc4 u0 com.mobibrw.myapplication/.SingleTopActivity t457}

可以看到出现两个单独的实例 Hist #2/Hist #1,而不是预期的忽略第二次调用。

在两次触发`startSingleTopWithFlagsActivity`/`startSingleInstanceWithFlagsActivity`的时候,只会出现了一个`Activity`,如下:

$ adb shell dumpsys activity | grep SingleTopActivity
        Hist #1: ActivityRecord{933a335 u0 com.mobibrw.myapplication/.SingleTopActivity t457}
          Intent { flg=0x420000 cmp=com.mobibrw.myapplication/.SingleTopActivity }
        Run #1: ActivityRecord{933a335 u0 com.mobibrw.myapplication/.SingleTopActivity t457}
    mResumedActivity: ActivityRecord{933a335 u0 com.mobibrw.myapplication/.SingleTopActivity t457}
  mFocusedActivity: ActivityRecord{933a335 u0 com.mobibrw.myapplication/.SingleTopActivity t457}

两者的区别就是

intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

这句代码。

还是学艺不精啊。

完整例子代码,点击此处下载 MyApplication

参考链接


发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注