在实际项目中我们应该根据特定的需求为每个活动指定恰当的启动模式。启动模式一共有四种,分别是 standard、singleTop、singleTask 和 singleInstance,可 以在 AndroidManifest.xml 中通 过给
标签 指定android:launchMode属性来选择启动模式。
standard
standard 是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。
Android 是使用返回栈来管理活动的,在 standard 模式(即默认情况)下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard 模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
使用之前的 ActivityTest 项目,修改 MainActivity 文件,代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("MainActivity -->> ",this.toString());
requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题栏
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,MainActivity.class);
startActivity(intent);
}
});
}
在 MainActivity 的基础上启动 MainActivity。另外我们还在 onCreate()方法中添加了一行打印信息,用于打印当前活动的实例。
运行程序,然后在 MainActivity 界面连续点击两次按钮,可以看到 LogCat 中打印信息如图所示。
从打印信息中我们就可以看出,每点击一次按钮就会创建出一个新的 MainActivity 实例。此时返回栈中也会存在三个MainActivity的实例,因此你需要连按三次Back键才能退出程序。
standard 模式的原理示意图,如图所示。
singleTop
当活动的启动模式指定为 singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
修改 AndroidManifest.xml 中 MainActivity 的启动模式,如下所示:
运行程序,查看LogCat 会看到已经创建了一个 MainActivity 的实例。
但是之后不管你点击多少次按钮都不会再有新的打印信息出现,因为目前 MainActivity 已经处于返回栈的栈顶,每当想要再启动一个 MainActivity 时都会直接使用栈顶的活动,因此 MainActivity 也只会有一个实例,仅按一次 Back 键就可以退出程序。
不过当 MainActivity 并未处于栈顶位置时,这时再启动 MainActivity,还是会创建新的实例的。修改 MainActivity 中 onCreate()方法的代码,如下所示:
Button button = (Button) findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
});
这次我们点击按钮后启动的是 SecondActivity。然后修改 SecondActivity 中 onCreate()方法的代码,如下所示:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("SecondActivity --->> ",this.toString());
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_second);
Button button2 = (Button) findViewById(R.id.button_2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SecondActivity.this,MainActivity.class);
startActivity(intent);
}
});
}
我们在 SecondActivity 中的按钮点击事件里又加入了启动 MainActivity 的代码。现在重新运行程序,在 MainActivity 界面点击按钮进入到 SecondActivity,然后在 SecondActivity 界面点击按钮,又会重新进入到 MainActivity。
可以看到系统创建了两个不同的 FirstActivity 实例,这是由于在 SecondActivity 中再次启动 MainActivity 时,栈顶活动已经变成了 SecondActivity,因此会创建一个新的 MainActivity 实例。现在按下 Back 键会返回到 SecondActivity,再次按下 Back 键又会回到 MainActivity,再按一次 Back 键才会退出程序。
singleTask
当活动的启动模式指定为 singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
修改 AndroidManifest.xml 中 MainActivity 的启动模式:
然后在 MainActivity 中添加 onRestart()方法,并打印日志:
@Override
protected void onRestart() {
super.onRestart();
Log.d("FirstActivity","onRestart");
}
最后在 SecondActivity 中添加 onDestroy()方法,并打印日志:
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("SecondActivity","onDestroy");
}
现在重新运行程序,在 MainActivity 界面点击按钮进入到 SecondActivity,然后在 SecondActivity 界面点击按钮,又会重新进入到 MainActivity。
查看 LogCat 中的打印信息
从打印信息中就可以明显看出了,在 SecondActivity 中启动 MainActivity 时,会发现返回栈中已经存在一个 MainActivity 的实例,并且是在 SecondActivity 的下面,于是SecondActivity 会从返回栈中出栈,而 MainActivity 重新成为了栈顶活动,因此 MainActivity 的 onRestart()方法和 SecondActivity 的 onDestroy()方法会得到执行。现在返回栈中应该只剩下一个 MainActivity 的实例了,按一下 Back 键就可以退出程序。
singleInstance
指定为 singleInstance模式的活动会启用一个新的返回栈来管理这个活动(其实如果 singleTask模式指定了不同的 taskAffinity,也会启动一个新的返回栈)。那么这样做有什么意义呢?想象以下场景,假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,应该如何实现呢?使用前面三种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用singleInstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。
修改 AndroidManifest.xml 中 SecondActivity 的启动模式:
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.activitytest.MY_CATEGORY" />
</intent-filter>
先将 SecondActivity 的启动模式指定为 singleInstance,然后修改 MainActivity 中 onCreate()方法的打印日志文件部分的代码:
Log.d("MainActivity -->> ","Task id 是:" + getTaskId());
然后修改 SecondActivity 中 onCreate()方法的代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("SecondActivity","Task id 是: " + getTaskId());
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_second);
Button button2 = (Button) findViewById(R.id.button_2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);
startActivity(intent);
}
});
}
同样在 onCreate()方法中打印了当前返回栈的 id,然后又修改了按钮点击事件的代码,用于启动 ThirdActivity。最后修改 ThirdActivity 中 onCreate()方法的代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("ThirdActivity","Task id 是: " + getTaskId());
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_third);
}
现在重新运行程序,在 MainActivity 界面点击按钮进入到 SecondActivity , 然后在 SecondActivity 界面点击按钮进入到 ThirdActivity。查看 LogCat 中的打印信息,如图所示。
可以看到,SecondActivity 的 Task id 不同于 MainActivity 和 ThirdActivity,这说明SecondActivity 确实是存放在一个单独的返回栈里的,而且这个栈中只有 SecondActivity 这一个活动。
然后我们按下 Back键进行返回,你会发现 ThirdActivity竟然直接返回到了 MainActivity,再按下 Back 键又会返回到 SecondActivity,再按下 Back 键才会退出程序由于 MainActivity 和 ThirdActivity 是存放在同一个返回栈里的,当在ThirdActivity 的界面按下 Back 键,ThirdActivity 会从返回栈中出栈,那么 MainActivity 就成为了栈顶活动显示在界面上,因此也就出现了从 ThirdActivity 直接返回到 MainActivity 的情况。然后在 MainActivity 界面再次按下 Back 键,这时当前的返回栈已经空了,于是就显示了另一个返回栈的栈顶活动,即 SecondActivity。最后再次按下 Back 键,这时所有返回栈都已经空了,也就自然退出了程序。