写在前面
Android中的Activity是可以层叠的。我们每启动一个新的活动,就会覆盖在原活动之上,然后点击Back键会销毁最上面的活动,下面的一个活动就会重新显示出来。
其实Android是使用任务(Task)来管理Activity的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish()方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。
下图展示了返回栈是如何管理Activity入栈出栈操作的。
Activity状态
①运行状态——位于返回栈的栈顶,系统最不愿意回收。
②暂停状态——Activity不再处于栈顶位置,但仍然可见(栈顶的Activity没有占满整个屏幕),处于暂停状态的Activity仍然是完全存活的,系统不愿将其回收,除非内存极低的情况。
③停止状态——当Activity不再处于栈顶且完全不可见,系统仍然会为其保存相应状态和成员变量,当其他地方需要内存时,处于停止状态的活动可能会被系统回收。
④销毁状态——当Activity从返回栈移除后变成销毁状态,系统倾向于回收处于该状态的Activity。
7个生命周期
1.完整生存期
Activity在onCreate()和onDestory()间所经历的。在onCreate()中完成各种初始化,在onDestory()中完成内存释放.
2.可见生存期
Activity在onStart()和onStop()间经历的。在该阶段Activity对用户是可见的,即使有可能无法与用户进行交互。在这两种方法下合理管理那些对用户可见的资源,比如在onStart()中对资源进行加载,在onStop()中对资源释放,保证处于停止状态的Activity不会占用过多内存。
3.前台生存期
Activity在onResume() 和 onPause()中。
Android官方提供了一张活动生命周期的示意图
回归正题
当我们多次启动同一个Activity时,系统会创建多个实例,并把它们按照先进后出的原则一一放入任务栈中,当我们按back键时,就会有一个activity从任务栈顶移除,重复下去,直到任务栈为空,系统就会回收这个任务栈。但是这样以来,系统多次启动同一个Activity时就会重复创建多个实例,这种做法显然不合理,为了能够优化这个问题,Android提供四种启动模式来修改系统这一默认行为。
Activity启动模式:
①standard
②singleTop
③singleTask
④singleInstance
方式:
1.在AndroidManifest.xml中通过给标签指定android:launchMode属性来选择启动模式
standard(默认)
standard是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。
Android是使用返回栈来管理活动的,在standard模式(即默认情况)下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
每次启动一个Activity都会重写创建一个新的实例,不管这个实例存不存在,这种模式下,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中。这个Activity它的onCreate(),onStart(),onResume()方法都会被调用。
standard模式的原理示意图
singleTop 栈顶复用模式
当活动的启动模式指定为singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
对比standard模式谈启动和后退:
清单文件:
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".OtherAty"
android:launchMode="singleTop" >
</activity>
standard模式下的MainActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("MainActivity", this.toString());
}
public void startSelf(View view){
Intent intent = new Intent();
intent.setClass(getApplicationContext(), MainActivity.class);
startActivity(intent);
}
public void startOther(View view){
Intent inte = new Intent();
inte.setClass(getApplicationContext(), OtherAty.class);
startActivity(inte);
}
singleTop的OtherAty:
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Log.e("OtherAty", this.toString());
setContentView(R.layout.other_aty);
}
public void startSelf(View view){
Intent intent = new Intent();
intent.setClass(getApplicationContext(), OtherAty.class);
startActivity(intent);
}
在MainActivity下单击启动自己后点击启动另一个(OtherAty),在OtherAty中点击启动自己,再点击后退键,再点击启动另一个,再点击后退,再点击启动自己,日志如下:
分析:
singleTask - 栈内复用模式
使用singleTop模式可以很好地解决重复创建栈顶活动的问题,但如果该活动并没有处于栈顶的位置,还是可能会创建多个活动实例的。
singleTask——让某个活动在整个应用程序的上下文中只存在一个实例。每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。
分析:
singleTask启动模式启动Activity时,首先会根据taskAffinity去寻找当前是否存在一个对应名字的任务栈:
- 如果不存在,则会创建一个新的Task,并创建新的Activity实例入栈到新创建的Task中去
- 如果存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例
如果存在实例,则将它上面的Activity实例都出栈,然后回调启动的Activity实例的onNewIntent方法 。
如果不存在该实例,则新建Activity,并入栈 。
singleInstance -全局唯一模式
singleInstance模式应该算是四种启动模式中最特殊也最复杂的一个了,不同于以上三种启动模式,指定为singleInstance模式的Activity会启用一个新的返回栈来管理这个Activity(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。
应用场景——AppA中有一个Activity允许其他的App调用,实现其他App和AppA可以共享这个Activity实例。前面三种启动模式肯定做不到,因为每个应用都会有各自的返回栈,同一个Activity在不同的返回栈中入栈时必然会创建新的实例。而singleInstance模式有一个单独的返回栈来管理这个Activity,不管是哪个应用程序来访问这个Activity,都共用同一个返回栈,也就解决了共享活动实例的问题。
该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
分析:
Activity的最佳实践
①知晓当前是在哪一个Activity
根据程序当前的界面就能判断出这是哪一个Activity;
②随时随地退出程序
需要用一个专门的集合类对所有的活动进行管理;还可以在销毁所有activity的代码后面再加上杀掉当前进程的代码,以保证程序完全退出。
③启动活动的最佳写法