众所周知,activity的启动模式有standard/singleTop、singleTask/singleInstance四种,之所以将四者两两分组,是因为她们都有相似的地方,下面就具体说说对这四种启动模式的理解
standard
即标准模式,也就是Android activity的的默认启动模式,每次启动一个activity都会创建一个新的实例,不管这个实例是否已经存在,新创建的activity实例的生命周期就是典型的activity的生命周期。
singleTop
栈顶复用模式,之所以将它跟standard放在一起,是因为当开启一个启动模式为singleTop的activity时,如果当前开启的activity在所需任务栈已存在实例,并且该实例位于栈顶(唯一条件),那么就不会创建新的activity示例,而是直接复用该栈顶的实例,并且会回调activity的onNewIntent方法。如果不是位于栈顶或者任务栈中根本不存在该实例则跟standard模式是一样的。举个例子来说,假设当前任务栈内的情况是ABCD,如果D的启动模式为singleTop,这时候再去启动D,则回复用D的示例,任务栈的情况还是ABCD,不会创建新的D的实例。如果启动A(或B或C),即使A(或B或C)也是singleTop模式,也不能进行实例复用,栈内情况为:ABCDA(或B或C)。再或者启动D,但D的启动模式是standard,则任务栈内的情况为ABCDD。
singleTask
栈内复用。根据singleTop,大概就知道singleTask是一个什么情况了。这种模式下,只要在Activity任务栈中存在,多次启动此Activity(当然要是同一个任务栈)都不会重新创建实例,和singleTop模式一样,系统也会回掉onNewIntent方法。具体来说,当一个具有singleTask模式的Activity A请求启动后,系统首先会寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,再讲A进栈。如果存在A所需的任务栈,这时就要看A的实例是否已经存在,如果存在,则复用A实例,并将A实例调到栈顶以及调用它的onNewIntent方法,并将之前在A上的其他Activity实例清空出栈。如果实例不存在,就创建A的实例并把A压入栈中。还是来举个例子:
1.假如已经存在任务栈S1,这时D以singleTask模式启动,但是D所需的任务栈并不是S1,而是S2,这是后会首先创建任务栈S2,然后将D的实例放进去。
2.第二种情况,还是接着第一种情况,D所需的任务栈就是S1,但是S1中并不存在D的实例,这是后D直接就被放进任务栈S1了。
3.第三种情况是,D所需的任务栈就是S1,并且当前S1栈内情况是ADBC,这时候以singleTask模式启动D,S1栈内情况将会变为AD,D被调到了栈顶,BC被清空出栈。同时回调onNewIntent方法。
我们都知道指定Activity的启动模式,有两种途径,分别是:
指定Activity的启动模式有两种方式,
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
singleInstance
它是单实例模式。它除了具有singleTask模式的所有特性外,通俗的说就是拥有singleInstance模式的Activity A在一个任务栈只会存在一个它的实例,并且不会存在其他Activity的实例。如果有后续的Activity的进来,如果是A则会栈内复用,不会创建新的实例。这里来举一个例子。假设有MainActivity、SecondActivity、ThirdActivity。其中SecondActivity是singleInstance模式,其他两个是standard模式
//启动SecondActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
//启动ThirdActivity
Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);
startActivity(intent);
这时在查看任务栈情况:
解释一下,由于SecondActivity是singleInstance模式,则启动它时,创建一个新的任务栈并将SecondActivity放了进去,当用SecondActivity启动其他Activity时,为了保证傲娇的SecondActivity独享任务栈,这是又要重新创建一个与SecondActivity不同的任务栈,将ThirdActivity放进去。所以这三个Activity都在不同的任务栈。
至于应用场景:
standard模式就是一般的正常的activity跳转。
singleTask适合作为程序入口activity。
singleTop适合接收通知启动的内容页面。比如:点击通知都是到达一个特定的页面,如果不使用复用就会有打开多个相同的页面。
singleInstance模式适合闹铃提醒,因为闹铃提醒跟其他的功能是分离的。
onNewIntent
当Activity的启动模式设置为singleTop(),singleTask(),singleInstance()时,重新启动该Activity时不会调用其onCreate()方法,而是调用onNewIntent(Intent intent)方法通过设置setIntent(intent)来刷新数据。
onSaveInstanceState
先看下官方文档的介绍:
/**
* Called to retrieve per-instance state from an activity before being killed
* so that the state can be restored in {@link #onCreate} or
* {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
* will be passed to both).
*
* This method is called before an activity may be killed so that when it
* comes back some time in the future it can restore its state. For example,
* if activity B is launched in front of activity A, and at some point activity
* A is killed to reclaim resources, activity A will have a chance to save the
* current state of its user interface via this method so that when the user
* returns to activity A, the state of the user interface can be restored
* via {@link #onCreate} or {@link #onRestoreInstanceState}.
*
*
Do not confuse this method with activity lifecycle callbacks such as
* {@link #onPause}, which is always called when an activity is being placed
* in the background or on its way to destruction, or {@link #onStop} which
* is called before destruction. One example of when {@link #onPause} and
* {@link #onStop} is called and not this method is when a user navigates back
* from activity B to activity A: there is no need to call {@link #onSaveInstanceState}
* on B because that particular instance will never be restored, so the
* system avoids calling it. An example when {@link #onPause} is called and
* not {@link #onSaveInstanceState} is when activity B is launched in front of activity A:
* the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't
* killed during the lifetime of B since the state of the user interface of
* A will stay intact.
*
*
The default implementation takes care of most of the UI per-instance
* state for you by calling {@link android.view.View#onSaveInstanceState()} on each
* view in the hierarchy that has an id, and by saving the id of the currently
* focused view (all of which is restored by the default implementation of
* {@link #onRestoreInstanceState}). If you override this method to save additional
* information not captured by each individual view, you will likely want to
* call through to the default implementation, otherwise be prepared to save
* all of the state of each view yourself.
*
*
If called, this method will occur before {@link #onStop}. There are
* no guarantees about whether it will occur before or after {@link #onPause}.
onSaveInstanceState主要是在Activity异常情况下保存Activity状态调用的方法,异常情况我们主要分析下面两种:
资源相关的系统配置发生改变导致Activity被杀死并重新创建
拿最简单的图片来说,当我们使用图片时会将图片放在drawable目录下,需要使用时就通过Resourse获取图片,同时为了适配各种屏幕的手机,也需要在drawable-hdpi、drawable-mdpi、drawable-xhdpi、drawable-xxhdpi放在对应分辨率的图片。当应用启动时,会根据当前设备去获取对应的图片。比如横屏手机和竖屏手机会拿到不同的图片,如果当前应用支持landscape和portrait时。如果当前Activity突然发生横竖屏切换,这个时候Activity就会被销毁进行重启。当然这种情况下我们也可以禁止Activity重启,通过配置configChanges参数。也可以在各个view中通过
android:saveEnabled
属性设置为false,或者通过滴啊勇setSaveEnabled方法显示阻止布局内的视图保存其状态。您通常不应该姜该属性停用,但是如果您想以不同方式恢复Activity UI的状态,就可能需要这样做。
onSaveInstanceState如果调用事发生在onStop方法之前,但是不能保证与onPause的执行顺序。
资源内存不足导致优先级低的Activity被杀死
Android按照优先级从高到低,可以分为三种:
- 1.前台Activity:正在与用户交互的Activity,优先级最高
- 2.可见但非前台Activity:比如Activity弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互
- 3.后台Activity:已经被暂停的Activity,执行了onStop,优先级最低
onRestoreInstanceState
/**
* This method is called after {@link #onStart} when the activity is
* being re-initialized from a previously saved state, given here in
* savedInstanceState. Most implementations will simply use {@link #onCreate}
* to restore their state, but it is sometimes convenient to do it here
* after all of the initialization has been done or to allow subclasses to
* decide whether to use your default implementation. The default
* implementation of this method performs a restore of any view state that
* had previously been frozen by {@link #onSaveInstanceState}.
*
* This method is called between {@link #onStart} and
* {@link #onPostCreate}.
*
* @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
*
* @see #onCreate
* @see #onPostCreate
* @see #onResume
* @see #onSaveInstanceState
*/
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
onRestoreInstanceState是在onStart方法之后调用。关于保存和恢复View的层次结构,系统的工作流程时这样的:首先Activity被意外终止时,Activity会调用onSaveIntsanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说它可能时DecorView。最后顶层容器再去意义通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层,父容器委托子元素去处理一件事情。当然还有很重要的一点就是,当父容器去一一通知子元素的时候,子元素必须得有对应的id,不然就没法找到,对应view数据就不能恢复
onConfigurationChanged
onConfigurationChanged对应着AndroidManifest中activity标签下的android:configChanges="",这里的取值为:
参考源码说明,
默认情况下,如果上表的配置发生改变,Activity将会被重启。有时为了当某些配置发生改变时,不让Activity重启,这时我们可以通过android:configChanges=""来指定一些配置,当它发生改变时,Activity不会重启,而只是回调了Activity的onConfigurationChanged方法。如果某些配置发生改变,而未在Activity的android:configChanges=""中,那么Activity还是会重启,而onConfigurationChanged就不会被调用。至于应用场景,加入我们应用的某些activity在屏幕方向发生改变时,这时不想让Activity进行重启,这时就可以在对应的Activity标签中申明android:configChanges="orientation|screenSize" ,然后当屏幕方向发生改变时,该Activity不会发生重启,而是回调onConfigurationChanged方法,然后我们就可以在这个方法里处理屏幕发生改变之后的情况。
FLAG_ACTIVITY_NEW_TASK
使用一个新的Task来启动一个Activity,新的Task表示Activity所需的Task,所以这是后有两种情况,一种是与启动的Activity同一Task,一种该Activity指定了taskAffinity字段,并且与启动它的Actvity TaskName不一样,这时才会重新新建一个Task。
FLAG_ACTIVITY_SINGLE_TOP
使用singleTop模式启动该Activity,与在AndroidManifest中指定android:launchMode=“singleTop”的效果是一样的
FLAG_ACTIVITY_CLEAR_TOP
使用singleTask模式启动该Activity,与在AndroidManifest中指定android:launchMode=“singleTask”的效果是一样的
FLAG_ACTIVITY_NO_HISTORY
Activity使用这种模式启动Activity,当该Activity启动其他Activity后,该Activity就消失了,不会保留在Activity栈中。