Android-Activity(四)启动模式

本文详细介绍了Android中Activity的四种启动模式:standard, singleTop, singleTask和singleInstance,通过实例展示了它们在启动和回退栈中的行为差异,帮助理解如何控制Activity的生命周期和实例管理。

在这里插入图片描述

启动模式一共有4种,分别是standard、singleTop、singleTask和singleInstance,可以在AndroidManifest.xml中通过给<activity>标签指定android:launchMode属性来选择启动模式。

standard

standard是Activity默认的启动模式,在不进行显式指定的情况下,所有Activity都会自动使用这种启动模式。在standard模式下,每当启动一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的Activity,系统不会在乎这个Activity是否已经在返回栈中存在,每次启动都会创建一个该Activity的新实例。
MainActivity:

package com.example.launchermodetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_main);
        Log.d("MainActivity", this.toString());

        Button buttonStartMain = findViewById(R.id.startMainActivity);
        buttonStartMain.setOnClickListener(view -> {
            Intent intent = new Intent(MainActivity.this, MainActivity.class);
            startActivity(intent);
        });
    }
}

layout_main.xml

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

    <Button
        android:id="@+id/startMainActivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start main activity again"
        />
</LinearLayout>

连续点击MainActivity的startMainActivity中的按钮,查看打印,通过 this.toString() 可以发现每次点击都会创建一个MainActivity活动实例:
在这里插入图片描述

singleTop

当Activity的启动模式指定为singleTop,在启动Activity时如果发现返回栈的栈顶已经是该Activity,则认为可以直接使用它,不会再创建新的Activity实例。如果当活动未处于栈顶时,仍然会创建出一个新的活动
在AndroidManifest.xml中MainActivity标签加入android:launchMode=“singleTop”,此时无论按多少次按钮都不会再次创建MainActivity活动,因为目前已经有一个MainActivity的活动处于栈顶的位置。此时MainActivity也只有一个实例,仅按一次Back键就可以退出程序。
在这里插入图片描述
然后再观察MainActivity不在栈顶的情况,添加FirstActivity,在MainActivity中打开FirstActivity,再从FirstActivity中返回MainActivity:
AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.launchermodetest">

    <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/Theme.LauncherModeTest">
        <activity
            android:name=".FirstActivity"
            android:exported="false" />
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

MainActivity:

package com.example.launchermodetest;

import androidx.appcompat.app.AppCompatActivity;

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

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_main);
        Log.d("MainActivity", this.toString());

        Button buttonStartMain = findViewById(R.id.startMainActivity);
        buttonStartMain.setOnClickListener(view -> {
            Intent intent = new Intent(MainActivity.this, MainActivity.class);
            startActivity(intent);
        });

        Button buttonStartFirst = findViewById(R.id.startFirstActivity);
        buttonStartFirst.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, FirstActivity.class);
                startActivity(intent);
            }
        });
    }
}

FirstActivity:

package com.example.launchermodetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;

public class FirstActivity extends AppCompatActivity {
    private static final String TAG = "FirstActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_first);
        Log.d(TAG, this.toString());

        Button buttonReturnMain = findViewById(R.id.returnMainActivity);
        buttonReturnMain.setOnClickListener(view -> {
            Intent intent = new Intent(FirstActivity.this, MainActivity.class);
            startActivity(intent);
        });

    }
}

layout_main.xml

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

    <Button
        android:id="@+id/startMainActivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start main activity again"
        />
    <Button
        android:id="@+id/startFirstActivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start First Activity"
        android:textAllCaps="false"
        />
</LinearLayout>

layout_first.xml

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

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Return Main Activity"
        android:id="@+id/returnMainActivity"
        />

</LinearLayout>

先启动MainActivity,从Main进入FirstActivity,再从First点击Return Main Activity按钮回到MainActivity,从打印可以看出MainActivity又重新创建了一个新的实例。返回栈此时存在三个Activity,你需要按三下返回才能退出。
在这里插入图片描述

singleTask

有没有什么办法可以让某个Activity在整个应用程序的上下文中只存在一个实例呢?这就要借助singleTask模式来实现了。当Activity的启动模式指定为singleTask,每次启动该Activity时,系统首先会在返回栈中检查是否存在该Activity的实例,如果发现已经存在则直接使用该实例,并把在这个Activity之上的所有其他Activity统统出栈,如果没有发现就会创建一个新的Activity实例。

在AndroidManifest.xml文件中的MainActivity标签加入android:launchMode=“singleTask”,再重复singTop模式中的操作,会发现MainActivity并不会创建新的实例,而是调用之前实例的onRestart()方法。此时返回栈中仅剩一个MainActivity实例,只需按一下Back即可退出。
在这里插入图片描述

singleInstance

不同于以上3种启动模式,指定为singleInstance模式的Activity会启用一个新的返回栈来管理这个Activity(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。
那么这样做有什么意义呢?想象以下场景,假设我们的程序中有一个Activity是允许其他程序调用的,如果想实现其他程序和我们的程序可以共享这个Activity的实例,应该如何实现呢?使用前面3种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个Activity在不同的返回栈中入栈时必然创建了新的实例。而使用singleInstance模式就可以解决这个问题,在这种模式下,会有一个单独的返回栈来管理这个Activity,不管是哪个应用程序来访问这个Activity,都共用同一个返回栈,也就解决了共享Activity实例的问题。

eg: 在MainActivity中启动FirstActivity,再在FirstActivity中启动SecondActivity中:

AndroidManifest.xml
指定FirstActivity为singleInstance模式

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.launchermodetest">

    <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/Theme.LauncherModeTest">
        <activity
            android:name=".SecondActivity"
            android:exported="false" />
        <activity
            android:name=".FirstActivity"
            android:exported="false"
            android:launchMode="singleInstance"/>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

MainActivity

package com.example.launchermodetest;

import androidx.appcompat.app.AppCompatActivity;

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

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_main);
        Log.d(TAG, "Task id is " + this.getTaskId());

        Button buttonStartMain = findViewById(R.id.startMainActivity);
        buttonStartMain.setOnClickListener(view -> {
            Intent intent = new Intent(MainActivity.this, MainActivity.class);
            startActivity(intent);
        });

        Button buttonStartFirst = findViewById(R.id.startFirstActivity);
        buttonStartFirst.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, FirstActivity.class);
                startActivity(intent);
            }
        });
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG,  this.toString() + " onRestart");
    }
}

FirstActivity

package com.example.launchermodetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;

public class FirstActivity extends AppCompatActivity {
    private static final String TAG = "FirstActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_first);
        Log.d(TAG, "Task id is " + this.getTaskId());

        Button buttonReturnMain = findViewById(R.id.returnMainActivity);
        buttonReturnMain.setOnClickListener(view -> {
            Intent intent = new Intent(FirstActivity.this, MainActivity.class);
            startActivity(intent);
        });

        Button buttonStartSecond = findViewById(R.id.startSecondActivity);
        buttonStartSecond.setOnClickListener(view -> {
            Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
            startActivity(intent);
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

SecondActivity

package com.example.launchermodetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("SecondActivity", "Task id is " + this.getTaskId());
        setContentView(R.layout.layout_second);
    }
}

layout_main.xml

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

    <Button
        android:id="@+id/startMainActivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start main activity again"
        />
    <Button
        android:id="@+id/startFirstActivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start First Activity"
        android:textAllCaps="false"
        />
</LinearLayout>

layout_first

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

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Return Main Activity"
        android:id="@+id/returnMainActivity"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Second Activity"
        android:id="@+id/startSecondActivity"
        />

</LinearLayout>

layout_second

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This is SecondActivity"
        />

</LinearLayout>

首先点击MainActivity中的startFirstActivity按钮,再点击FirstActivity中的startSecondActivity按钮,再在SecondActivity中点击按钮,通过activity对象getTaskId()查看Task的id,可以看出MainActivity和SecondActivity的Task id 是47,而FirstActivity的Task id是48,所以FirstActivity确实在单独的一个栈中。

在这里插入图片描述

这时点击Back键会先从SecondActivity回到MainActivity中,再点击Back键会回到FirstActivity中,singleInstance的模式示意图如下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值