在实际的项目中我们应该根据特定的需求为每个活动指定恰当的启动模式。
启动模式一共有4种。standard、singleTop、singleTask和singleInstance
通过在AndroidManifest.xml中给
标签指定android:launchMode
属性来选择启动模式。
1. 启动模式一:standard
活动的默认启动模式。在该模式下,每当启动一个新活动,它就会在返回栈中入栈,并处于栈顶的位置,并且,不管此活动是否已经存在于返回栈中,每次启动都会创建该活动的一个新实例。
Demo
这里我们在第一个项目(活动的跳转)的基础上进行讲解
在FirstActivity中的onCreate()方法中加入Log.d("FirstActivity",this.toString());
,用于在创建活动实例时打印信息;在按钮点击函数start_secondavtivity()中更改代码实现在FirstActivity中打开FirstActivity
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("FirstActivity",this.toString());
setContentView(R.layout.activity_first);
}
public void start_secondactivity(View view){
Intent intent = new Intent(FirstActivity.this,FirstActivity.class);
startActivity(intent);
}
}
运行程序,然后点击两次按钮并查看打印信息:
程序初运行会创建一个FirstActivity实例,并且每点击一次按钮就会创建一个新的FirstActivity实例,从打印信息中我们可以看出存在3个不同的FirstActivity,因此需要连按3次back键才能推出程序。
standard模式原理示意图:
2. 启动模式二:singleTop
在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
Demo
修改AndroidManifest.xml中的FirstActivity的启动模式
android:launchMode="singleTop"
然后重新运行程序
我们发现不管点击多少次按钮,都只有一次打印信息,,因为目前FirstActivity已经处于返回栈的栈顶,每当想要再启动一个FirstActivity时都会直接使用栈顶的活动,因此FirstActivity只有一个实例,仅按一次back键就可退出程序。
不过当FirstActivity并未处于栈顶时,若再启动FirstActivity还是会创建新的实例。
下面来实验一下,修改FirstActivity中按钮响应的代码,将按钮跳转改为SecondActivity
public void start_secondactivity(View view){
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
}
然后在SecondActivity中添加按钮函数start_firstactivity(),用来启动FirstActivity(注意先在布局中加入button控件),并在onCreate()中打印信息
activity_second.xml布局
SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("SecondActivity",this.toString());
setContentView(R.layout.activity_second);
}
public void start_firstactivity(View view){
Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
startActivity(intent);
}
}
现在我们运行程序查看结果:
在FirstActivity中点击按钮启动SecondActivity,接着又在SecondActivity中点击按钮启动FirstActivity
可以看到系统首先创建一个FirstActivity实例,我们点击按钮后系统又创建出一个SecondActivity实例,此时栈顶活动变为SecondActivity,再点击SecondActivity中的按钮,系统会创建一个新的FirstActivity实例。现在按下返回键会回到SecondActivity,再次按下返回键回到第一个FirstActivity,再按一次才退出程序。
singleTop模式原理示意图:
3. 启动模式三:singleTask
使用 single Top模式可以很好地解决重复创建栈顶活动的问题,但是如你在上一节所看到的,如果该活动并没有处于栈顶的位置,还是可能会创建多个活动实例的。那么有没有什么办法可以让某个活动在整个应用程序的上下文中只存在一个实例呢?这就要借助 singleTask模式来实现了。当活动的启动模式指定为 singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
Demo
修改AndroidManifest.xml中的FirstActivity的启动模式为singleTask
然后在FirstActivity中添加onRestart()方法,并打印日志:
@Override
protected void onRestart(){
super.onRestart();
Log.d("FirstActivity","onRestart");
}
在SecondActivity中添加onDestroy()方法,并打印日志:
@Override
protected void onDestroy(){
super.onDestroy();
Log.d("SecondActivity","onDestroy");
}
重新运行程序,在FirstActivity中点击按钮进入SecondActivity,然后在SecondActivity中点击按钮进入FirstActivity,查看日志:
其实从打印信息中就可以明显看出了,在 SecondActivity中启动 FirstActivity时,会发现返回栈中已经存在一个FirstActivity的实例,并且在SecondActivity的下面,于是SecondActivity会从返回栈中出栈,而 FirstActivity重新成为了栈顶活动,因此 FirstActivityI的 onRestart()方法和 SecondActivity的 onDestroy()方法会得到执行。现在返回栈中应该只剩下一个 FirstActivityI的实例了,按一下Back键就可以退出程序。
singleTask模式原理示意图:
4. 启动模式四:singleInstance
singleInstance模式算是4种启动模式中最复杂的一个了,不同于以上三种模式,该模式下活动会启用一个新的返回栈来管理这个活动(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。
那么这样做有什么意义呢?想象以下场景,假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,应该如何实现呢?使用前面3种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用singlelnstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。
为了帮助你更好地理解这种启动模式,我们还是来实践一下。
Demo
首先创建一个新活动ThirdActivity及布局activity_third
Log.d("ThirdActivity","Task id is "+getTaskId());
用于打印当前返回栈的id
public class ThirdActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("ThirdActivity","Task id is "+getTaskId());
setContentView(R.layout.activity_third);
}
}
修改FirstActivity中onCreate()打印日志代码为Log.d("FirstActivity","Task id is "+getTaskId());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("FirstActivity","Task id is "+getTaskId());
setContentView(R.layout.activity_first);
}
SecondActivity中同样修改这句代码,并使按钮响应事件改为跳转至ThirdActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("SecondActivity","Task id is "+getTaskId());
setContentView(R.layout.activity_second);
}
public void start_firstactivity(View view){
//Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);
startActivity(intent);
}
最后修改 AndroidManifest.xml中SecondActivity的启动模式为singleInstance
现在运行程序,在FirstActivity中进入SecondActivity,从SecondActivity中进入ThirdActivity ,查看日志
可以看到, SecondActivity的Task id不同于FirstActivity和ThirdActivity,这说明SecondActivity确实是存放在一个单独的返回栈里的,而且这个栈中只有SecondActivity这一个活动。
然后我们按下Back键进行返回,你会发现ThirdActivity竟然直接返回到了FirstActivity,再按下Back键又会返回到SecondActivity,再按下Back键才会退出程序,这是为什么呢?
其原理很简单,由于FirstActivity和ThirdActivity是存放在同一个返回栈里的,当在ThirdActivity的界面按下Back键,ThirdActivity会从返回栈中出栈,那么FirstActivity就成为了栈顶活动显示在界面上,因此也就出现了从ThirdActivity直接返回到FirstActivity的情况。然后FirstActivity界面再次按下Back键,这时当前的返回栈已经空了,于是就显示了另一个返回栈的栈顶活动,即SecondActivity。最后再次按下Back键,这时所有返回栈都已经空了,也就自然退出了程序。