1、序言
作为这本书的第一章,主席还是把Activity搬上来了,也确实,和Activity打交道的次数基本上是最多的,而且他的内容和知识点也是很多的,非常值得我们优先把他掌握,Activity中文翻译过来就是”活动”的意思,但是主席觉得这样翻译有些生硬,直接翻译成“界面”可能更好,的确,Activity主要也是用于UI效果的呈现,不过两者翻译都不为过,我们知其意就行了,正常情况下,我们除了Window,Dialog,Toast,我们还能见到的就只有Activity了,他需要setContentView()去绑定一个视图View作为效果的呈现,然而,作为一本高质量的进阶书。肯定不会去围绕着入门知识讲解,本章的侧重点在于对Activity使用过程中搞不清楚的概念,生命周期和启动模式已经IntentFilter的匹配规则分析,毕竟Activity在异常状态下的生命周期是多样化的,至于Activity的启动模式和各种各样的Flags,更是让很多人摸不着头脑,还有隐式启动Activity中也有着复杂的Intent匹配过程,所以我们还是一步步的去学习下去,真正的了解Activity这个小家伙!
Activity的生命周期,本章主要讲解两个方面
典型情况下的生命周期
异常情况下的生命周期
典型情况是指用户参与的情况下,Activity所经过的生命周期的变化,异常情况下的话,就有多种可能了,比如系统回收或者由于当前设备的Configuration发生改变从而导致Activity被销毁重建,异常情况下的生命周期的关注点和典型情况下有些不同,所以要分开来描述才能描述的清楚些
2、典型情况下的生命周期分析
在正常的情况下,生命周期会经历以下的生命周期
onCreate:表示Activity正在被创建,这是生命周期的第一个方法,在这个方法中,我们可以做一些初始化的工作,比如调用onContentView去加载界面布局资源,初始化Activity所需数据等
onRestart:表示Activity正在重新启动,一般情况下,当当前Activity从不可见重新变为可见时,onRestart就会被调用,这总情况一般是用户行为所导致的,比如用户按home键切换到桌面或者用户打开了一个新的Activity,这时当前的Activity就会被暂停,也就是onPause和onStop方法被执行了,接着用户又回到了这个Activity,就会出现这种情况
onStart:表示Activity正在被启动,即将开始,这个时候Activity已经可见了,但是还没有出现在前台,还无法和用户交互,这个时候我们可以理解为Activity已经启动了,但是我们还没有看见
onResume:表示Activity已经可见了,并且出现在前台,并开始活动了,要注意这个和onStart的对比,这两个都表示Activity已经可见了,但是onStart的时候Activity还处于后台,onResume的时候Activity才显示到前台
onPause:表示Activity正在停止,正常情况下,紧接着onStop就会被调用,在特殊情况下,如果这个时候再快速的回到当前Activity,那么onResume就会被调用,主席的理解是这个情况比较极端,用户操作很难重现这个场景,此时可以做一些数据存储,停止动画等工作,但是注意不要太耗时了,因为这样会影响到新的Activity的显示,onPause必须先执行完,新Activity的onResume才会执行
onStop:表示Activity即将停止,同样可以做一些轻量级的资源回收,但是不要太耗时了
onDestroy:表示Activity即将被销毁,这是Activity生命周期的最后一个回调,在这里我们可以做一些最后的回收工作和资源释放
示意图如下
备注:
1.针对一个特定的Activity,第一次启动,回调如下:onCreate ——> onStart ——> onResume
2.当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause ——> onStop ——> 这里有一种特殊的情况就是,如果新的Activity采取了透明的主题的话,那么当前Activity不会回调onStop
3.当用户再次回到原来的Activity,回调如下:onRestart ——> onStart ——> onResume
4.d当用户按back键的时候回调如下:onPause ———> onStop ——> onDestroy
5.当Activity被系统回收的时候再次打开,生命周期回调方法和1是一样的,但是你要注意一下就是只是生命周期一样,不代表所有的进程都是一样的,这个问题等下回详细分析
6.从整个生命周期来说,onCreate和onDestroy是配套的,分别标示着Activity的创建和销毁,并且只可能有一次调用,从Activity是否可见来说,onStart和onStop是配套的,随着用户的操作和设备屏幕的点亮和熄灭,这两个方法可能被调用多次,从Activity是否在前台来说,onResume和onPause是配套的,随着用户操作或者设备的点亮和熄灭,这两个方法可能被多次调用
Question:这里提出两个问题
1.onStart和onResume,onPause和onStop从描述上都差不多,对我们来说有什么实质性的不同呢?
我们先来回答第一个问题,从实际使用过程来说,
onStart和onResume,onPause和onStop看起来的确差不多,甚至我们可以只保留其中的一对,比如只保留onStart和onStop,既然如此,那为什么Android系统还会提供看起来重复的接口呢?根据上面的分析,我们知道,这两个配对的回调分别代表不同的意义,onStart和onStop是从Activity是否可见这个角度来回调的,除了这种区别,在实际的使用中,没有其他明显的区别
2.假设当前Activity为A,如果用户打开了一个新的Activity为B,那么B的onResume和A的onPause谁先执行呢?
第二个问题,我们就要从源码的角度来分析以及得到解释了,关于Activity的工作原理会在本书后续章节进行讲解,这里我们大致的了解即可,从Activity的启动过程来看,我们来看一下系统的源码,Activity启动过程的源码相当复杂,设计到了Instrumentation,Activit和ActivityManagerService(AMS),这里不详细分析这一过程。
简单理解,启动Activity的请求会由 Instrumentation来处理,然后他通过Binder向AMS发请求,AMS内部维护着一个ActivityStack,并负责栈内的Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期方法的调用,在ActivityStack中的resumeTopActivityLnnerLocked方法中,有这么这段代码
boolean dontWaitForPause = (next.info.flags& ActivityInfo.FLAG_RESUME_WHILE_PAUSING)!=0;
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, KeyStore.TrustedCertificateEntry,dontWaitForPause);
if(mResumedActivity != null){
pausing != startPaUSINGlOCAKED(userLeaving,false,true,dontWaitForPause);
if(DEBUG_STATES){
Slog.d(TAG,"resumeTopActivityLocked:pausing" + mResumedActivity);
}
}
从上述的代码中我们可以看出,在新Activity启动之前,栈顶的Activity需要先onPause后,新的Activity才能启动,最终,在ActvityStackSupervisor中的realStartActivityLocked方法中,会调用如下代码
app.thread.scheduleLaunchActivity(new Intent(r.intent),r.appToken,System.identityHashCode(r),r.info,new Configuration(mService.mConfiguration)
,r.compat,r.task.voiceInteractor,app.repProcState,r.icicle,r.persistentState,results,new Intents,!andResume,mService.isNextTransitionForward()
,profilerInfo);
我们都知道,在这个app.thread的类型是IApplicationThread的具体实现实在ActivityTread中,所以,这段代码实际上遇到了ActivityThread当中,,即ApplicationThread的scheduleLaunchActivity方法,而scheduleLaunchActivity方法最终会完成生命周期的调用过程。
因此可以得出结论,是旧Activity先onPause,然后新的Activity再启动 至于ApplicationThread的scheduleLaunchActivity方法为什么会完成新Activity的生命周期,请看接下来的代码,scheduleLaunchActivty为什么会完成新的Activty
private void handlerLaunchActivity(ActivityClientRecord r, Intent customIntent){
//if we are getting ready to gc after going to the background,well we are back active so skip it
unscheduleGcIdler();
mSomeActivitiesChanged =true;
if(r.profilerInfo != null){
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling;
}
//Make sure we are running with the most recent config
handlerConfigurationChanged(null,null);
if(localLOGV)Slog.v
TAG,"Handling launch of"+r);
//在这里新Activity被创建出来,其onCreate和onStart被调用
Activity a = PerformLaunchActivity(r,customIntent);
if(a != null){
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.start;
handlerResumeActivity(r.token,false,r.isForward,
!r.activity.mFinished && r.startsNotResumed);
}
//省略...
}
从上面的分析可以看出,当新的Activity启动的时候,旧的Activity的onPause方法会先执行,然后才启动新的Activity,到底是不是这样尼?我们可以写一个小栗子来验证一下,如下是两个Activity的代码,在MainActivity中点击按钮可以跳转到SecondActivity,同时为了分析生命周期,我们把log日志也打出来
MainActivity
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnTo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop");
}
}
SecondActivity
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.i(TAG, "onCreate");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume");
}
}
3、异常情况下的生命周期分析
上一节我们分析的是正常事情下的生命周期,但是我们写程序也不要理想化,居多的问题就是出在异常情况下,我们知道,Activity除了受用户操作导致的正常生命周期的调度,同时还会存在一些异常的情况,比如当资源相关的系统配置发生改变以及系统内存不足的时候,Activity就有可能被杀死,下面我们具体来分析下这几种情况
情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建
理解这个问题,首先要对系统的资源加载有一定的了解,这里就不详细分析系统资源加载的机制了,但是我们简单说明一下,拿最简单的图片来说,当我们把一张图片挡在drawable中的时候,就可以通过Resources去获取这张图片了,同时为了兼容不同的设备,我们可能还需要在其他一些目录下放置不同的图片,比如drawable-xhdpi之类的,当应用程序启动时,系统会根据当前设备的情况去加载合适的Resources资源,比如说横屏手机和竖屏手机会拿着两张不同的图片(设定了landscape或者portrait状态下的图片),比如之前Activity处于竖屏,我们突然旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity会被销毁并且重新创建,当然我们也可以阻止系统重新创建我们的Activity
默认情况下,如果我们的Activity不做特殊处理,那么当系统配置发生改变之后,Activity就会销毁并且重新创建,生命周期如下图
当系统配置发生改变的时候,Activity会被销毁,其onPause,onStop,onDestroy均会被调用,同时由于Activity是异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态,这个方法调用的时机是在onStop之前,他和onPause没有既定的时序关系,他即可能在onPause之前调用,也有可能在之后调用,需要强调的是,这个方法只出现在Activity被异常终止的情况下,正常情况下是不会走这个方法的。
当我们onSaveInstanceState保存到Bundler对象作为参数传递给onRestoreInstanceState和onCreate方法,因此我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建。如果被重建了,我们就取出之前的数据恢复,从时序上来说,onRestoreInstanceState的调用时机应该在onStart之后
同时我们要知道,在onSaveInstanceState和onRestoreInstanceState方法中,系统自动为我们做了一些恢复工作,当Activity在异常情况下需要重新创建时,系统会默认我们保存当前的Activity视图架构,并且为我们恢复这些数据,比如文本框中用户输入的数据,ListView滚动的位置,这些View相关的状态系统都会默认恢复,具体针对某一个特定的View系统能为们恢复那些数据?我们可以查看View的源码,和Activity一样,每一个View都有onSaveInstanceState和onRestoreInstanceState这两个方法,看一下他们的实现,就能知道系统能够为每一个View恢复数据
关于保存和恢复View的层次结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托上面的顶级容器去保存数据,顶级容器是一个ViewGroup,一般来说他可能是一个DecorView,最后顶层容器再去一一通知他的子元素来保存数据,这样整个数据保存过程就完成了,可以发现,这是一种典型的委托思想,上层委托下层,父容器委托子容器,去处理一件事件,这种思想在Android 中有很多的应用,这里就不再重复介绍了,接下来举个例子,那TextView来说,我们分析一下他到底保存了那些数据
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
boolean save = mFreezesText;
int start = 0;
int end = 0;
if (mText != null) {
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
save = true;
}
}
if (save) {
SavedState ss = new SavedState(superState);
// XXX Should also save the current scroll position!
ss.selStart = start;
ss.selEnd = end;
if (mText instanceof Spanned) {
Spannable sp = new SpannableStringBuilder(mText);
if (mEditor != null) {
removeMisspelledSpans(sp);
sp.removeSpan(mEditor.mSuggestionRangeSpan);
}
ss.text = sp;
} else {
ss.text = mText.toString();
}
if (isFocused() && start >= 0 && end >= 0) {
ss.frozenWithFocus = true;
}
ss.error = getError();
return ss;
}
return superState;
}
通过上图看出,我们采用了旋转屏幕来终止Activity,在我们旋转屏幕以后,Activity被销毁重建,我们输入的文本被正确还原了,说明我们的系统能够正确的做一些View层的分析,我们看下代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String test = savedInstanceState.getString("extra_test");
Log.i(TAG, test);
}
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
Log.i(TAG, "onSaveInstanceState");
outState.putString("extra_test", "test");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String test = savedInstanceState.getString("extra_test");
Log.i(TAG, test);
}
上面的代码很简单,首先我们在onSaveInstanceState中保存一个字符串,然后当我们的Activity被销毁并且重新创建之后,我们再去获取之前存储的字符串,接收的位置可以选择onRestoreInstanceState或者onCreate,两者的区别是:onRestoreInstanceState一旦被调用,其参数Bundler savedInstanceState一定有值,我们不用额外的判断是否为空但是onCreate不行,onCreate如果正常启动的话,其参数Bundler onSaveInstanceState为null,所以需要一些额外的判断,这两个方法我们选择任意一个都是可以进行数据恢复的,但是关键建议我们使用onRestoreInstanceState去恢复数据,下面我们看一下运行的日志:
如上图所示,Activity销毁后调用了onSaveInstanceState来保存数据,重新创建以后再onCreate和onRestoreInstanceState中能恢复数据,这个正好证明了我们刚才的分析,针对onSaveInstanceState我们有一点要说明,那就是系统只会在即将被销毁并且有机会重新显示的情况下才会去调用它,考虑到这一种情况,当Activity正常销毁的时候,系统不会调用onSaveInstanceState,因为被销毁的Activity不可能再次被显示出来,这句话不好理解,但是我们可以对比一下旋转屏幕所造成的Activity异常销毁,这个过程和正常停止的Activity是不一样的,因为旋转屏幕之后,Activity被销毁的同时会立即创建新的Activity实例,这个时候Activcity有机会再次立刻显示,所以系统进行了数据存储,这里可以简单的这么理解,系统只在Activity异常终止的情况下才会调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,其他情况不会触发。
情况2:资源内存不足导致低优先级的Activity被杀死
这个情况我们不好模拟,但是其数据的存储和恢复过程和情况一是一致的,这里我们描述一下Activity的优先级情况,Activity按照优先级的从高往低,可以分为三种:
1.前台Activity:正在和用户交互的Activity,优先级最高
2.可见但非前台Activity:比如对话框,导致Activity可见但是位于后台无法和用户直接交互
3.后台Activity:已经被暂停的Activity,比如执行了onStop,优先级最低
当系统内存不足的时候,系统就会按照上述优先级去杀死目标Activity所在的进程,并且在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱离四大组建而独立运行在后台中,这样进程很容易就被杀死了,比较好的方法就是将后台工作放在Service中从而保证了进程有一定的哟徐爱你集,这样就不会轻易的被杀死
上面分析了系统的数据存储和恢复机制,我们知道,当系统配置发生改变后,Activity会被重新创建,那我们有没有什么办法不重新创建尼?答案是有的,接下来我们来分析一下这个问题,系统配置中有很多内容,如果当某项内容发生改变后,我们不想系统重新创建,就可以给configChangs属性加上orientation这个值
android:configChanges="orientation"
如果想指定多个值的话可以用“|”连接起来