在实际项目中我们应该根据特定的需求为每个活动指定恰当的启动模式。本博文在ActivityTest项目的基础上作进一步的开发。
目录
Activity的启动模式
启动模式一共有4种,分别是standard、singleTop、singleTask和singleInstance,可以在AndroidManifest.xml中通过给<activity> 标签指定android:launchMode 属性来选择启动模式
standard
standard是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。因此,之前博客中代码全部都属于这类。
Android是使用返回栈来管理活动的,在standard模式(即默认情况)下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
修改FirstActivity中onCreate() 方法的代码,如下所示:
package com.example.activitytest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class FirstActivity extends AppCompatActivity {
//重写onCreateOptionsMenu方法,将菜单显示出来
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
//通过getMenuInflater() 方法能够得到MenuInflater 对象
//再调用它的inflate() 方法就可以给当前活动创建菜单了
//inflate() 方法接收两个参数,
//第一个参数用于指定我们通过哪一个资源文件来创建菜单,这里当然传入R.menu.main 。
//第二个参数用于指定我们的菜单项将添加到哪一个Menu 对象当中,这里直接使用onCreateOptionsMenu() 方法中传入的menu 参数。
return true;
//返回true ,表示允许创建的菜单显示出来
//若返回了false ,创建的菜单将无法显示。
}
//重写onOptionsItemSelected方法,定义菜单的相应事件
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){ //通过调用item.getItemId() 来判断我们点击的是哪一个菜单项
case R.id.add_item:
Toast.makeText(FirstActivity.this, "你点了add!",Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(FirstActivity.this, "你点了remove!",Toast.LENGTH_SHORT).show();
break;
default:
break;
}
return true;
}
************************************///
//在seconactivity被销毁后,回调上一个活动的onActivityResult()方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//三个参数
//1、requestCode ,即我们在启动活动时传入的请求码。
//2、resultCode ,即我们在返回数据时传入的处理结果
//3、data ,即携带着返回数据的Intent
switch (requestCode) {
//由于在一个活动中有可能调用startActivityForResult() 方法去启动很多不同的活动
// 每一个活动返回的数据都会回调到onActivityResult() 这个方法中
//因此先通过 requestCode来检测数据的来源
case 1:
if (resultCode == RESULT_OK) {//resultCode判断处理结果是否成功
String returnedData = data.getStringExtra("data_return");//从data中取值
Log.d("FirstActivity", returnedData);
}
break;
default:
}
}
//活动的初始化
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("FirstActivity",this.toString());
setContentView(R.layout.first_layout);//通过文件名字来调用,加载布局
//setContentView方法用于加载布局
//项目中添加的任何资源都会在R文件中生成一个相应的资源id
// 因此,所创建的layout文件会自动将id添加到R文件中
// 只需要调用R.layout.first_layout 就可以得到first_layout.xml 布局的id
///**********************///
Button button1=(Button) findViewById(R.id.button_1);
//通过findViewById() 方法获取到在布局文件中定义的元素
//在first_layout文件中,通过android:id="@+id/button_1"定义了按钮
//findViewById() 方法返回的是一个View 对象,我们需要向下转型将它转成Button 对象
button1.setOnClickListener(new View.OnClickListener() {
@Override //表示重写
public void onClick(View v) {
Intent intent=new Intent(FirstActivity.this, FirstActivity.class);//采用显式启动
startActivity(intent);//启动活动
}
});
}
}
在firstactivity中再次启动firstactivity
每点击一次按钮就会创建出一个新的FirstActivity实例。此时返回栈中也会存在3个FirstActivity的实例,因此你需要连按3次Back键才能退出程序。其模式的示意图如下图所示
singleTop
当活动的启动模式指定为singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
修改AndroidManifest.xml中FirstActivity的启动模式
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitytest">
<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=".ThirdActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.ActivityTest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.ActivityTest.MY_CATEGORY" />
</intent-filter>
</activity>
<activity android:name=".FirstActivity"
android:launchMode="singleTop"
android:label="This is FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
不管你点击多少次按钮都不会再有新的打印信息出现,因为目前FirstActivity已经处于返回栈的栈顶,每当想要再启动一个FirstActivity时都会直接使用栈顶的活动,因此FirstActivity也只会有一个实例,仅按一次Back键就可以退出程序。
其工作模式如下图所示
singleTask
使用singleTop模式可以很好地解决重复创建栈顶活动的问题,但是,如果该活动并没有处于栈顶的位置,还是可能会创建多个活动实例的。还是重复的用了资源创建
那么为了让某个活动在整个应用程序的上下文中只存在一个实例呢,就要借助singleTask模式来实现了。当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
只需将AndroidManifest.xml中FirstActivity的启动模式launchMode改为
android:launchMode="singleTask"
示意图如下图所示
singleInstance
指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,使用前面3种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用singleInstance模式就可以解决这个问
题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。
只需将AndroidManifest.xml中的启动模式launchMode改为
android:launchMode="singleInstance">
按下Back键进行返回,会发现ThirdActivity竟然直接返回到了FirstActivity,再按下Back键又会返回到SecondActivity,再按下Back键才会退出程。这是由于FirstActivity和ThirdActivity是存放在同一个返回栈里的,当在ThirdActivity的界面按下Back键,ThirdActivity会从返回栈中出栈,那么FirstActivity就成为了栈顶活动显示在界面上,因此也就出现了从ThirdActivity直接返回到FirstActivity的情况。然后在FirstActivity界面再次按下Back键,这时当前的返回栈已经空了,于是就显示了另一个返回栈的栈顶活动,即SecondActivity。最后再次按下Back键,这时所有返回栈都已经空了,也就自然退出了程序。过程如下图所示。