赞
踩
Activity的生命周期分为两部分内容,一部分是典型情况下的生命周期,另一部分是异常情况下的生命周期。典型情况下的生命周期指在有用户参与的情况下,Activity所经过的生命周期的改变;而异常情况下的生命周期是指Activity被系统回收或者由于当前设备的Configuration发生改变从而导致Activity被销毁重建,异常情况下的生命周期的关注点和典型情况下略有不同。
ON_START
ON_PAUSE
状态,但是仍然可见;当再次回到原activity的时候,就会回调onResume方法了。ON_PAUSE
状态,并进入后台。一般的使用场景为界面进入后台时的轻量级资源释放。但是!!仍然是可见的,只是无法进行交互。ON_STOP
状态,并回调onStop方法。同时,新activity创建的界面是在onResume方法之后才显示出来,所以onStop方法会在新activity的onResume方法回调之后再被回调。也可以这么说,新启动的activity并不会等待旧Activity的onStop执行完毕之后再显示。因而如果onStop方法里做一些比较耗时的操作也不会导致被启动的activity启动延迟。onStop方法的目的就是做资源释放操作。因为是在另一个activity显示之后再被回调,所以这里可以做一些相对重量级的资源释放操作,如中断网络请求、释放相机资源等。我们还应使用 [onStop()](https://developer.android.com/reference/android/app/Activity?hl=zh-cn#onStop())
执行 CPU 相对密集的关闭操作。例如,如果无法找到更合适的时机来将信息保存到数据库,可以在 [onStop()](https://developer.android.com/reference/android/app/Activity?hl=zh-cn#onStop())
期间执行此操作。在 [onStop()](https://developer.android.com/reference/android/app/Activity?hl=zh-cn#onStop())
方法中,应用应释放或调整在应用对用户不可见时的无用资源。例如,应用可以暂停动画效果,或从精确位置更新切换到粗略位置更新。使用 [onStop()](https://developer.android.com/reference/android/app/Activity?hl=zh-cn#onStop())
而非 [onPause()](https://developer.android.com/reference/android/app/Activity?hl=zh-cn#onPause())
可确保与界面相关的工作继续进行,即使用户在多窗口模式下查看我们的 Activity 也能如此。启动Activity的请求会由Instrumentation来处理,然后它通过Binder向AMS发请求,AMS内部维护着一个ActivityStack并负责栈内的Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期方法的调用。ActivityStack中的resumeTopActivity-InnerLocked方法
// We need to start pausing the current activity so the top one
// can be resumed...
boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_
WHILE_PAUSING) ! = 0;
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true,
dontWaitForPause);
if (mResumedActivity ! = null) {
pausing |= startPausingLocked(userLeaving, false, true, dontWait-
ForPause);
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " +
mResumedActivity);
}
在新Activity启动之前,桟顶的Activity需要先onPause后,新Activity才能启动。最终,在ActivityStackSupervisor中的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, newIntents, ! andResume, mService.isNextTransition-
Forward(),
profilerInfo);
这个app.thread的类型是IApplicationThread,而IApplicationThread的具体实现是ActivityThread中的ApplicationThread。所以,这段代码实际上调到了ActivityThread的中ApplicationThread的scheduleLaunchActivity
方法,在其中最终会完成新Activity的onCreate、onStart、onResume的调用过程。因此,可以得出结论,是旧Activity先onPause,然后新Activity再启动。至于ApplicationThread的scheduleLaunchActivity方法为什么会完成新Activity的onCreate、onStart、onResume的调用过程,请看下面的代码。scheduleLaunchActivity最终会调用如下方法,而如下方法的确会完成onCreate、onStart、onResume的调用过程。
源码:ActivityThread# handleLaunchActivity
private void handleLaunchActivity(ActivityClientRecord r, Intent custom-
Intent) {
// 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.
handleConfigurationChanged(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.state;
//这里新Activity的onResume会被调用
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && ! r.startsNotResumed);
//省略
}
从上面的分析可以看出,当新启动一个Activity的时候,旧Activity的onPause会先执行,然后才会启动新的Activity。
系统的资源加载机制,拿图片来说,我们把可以通过Resources去获取drawable目录下的图片。同时为了兼容不同的设备,我们可能还需要在其他一些目录放置不同的图片,比如drawable-mdpi、drawable-hdpi、drawable-land等。当应用程序启动时,系统就会根据当前设备的情况去加载合适的Resources资源,比如说横屏手机和竖屏手机会拿到两张不同的图片(设定了landscape或者portrait状态下的图片)。
在默认情况下,当系统配置发生改变后,Activity就会被销毁并重新创建,系统会调用onSaveInstanceState来保存当前Activity的状态。当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。因此,我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建了,如果被重建我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用时机在onStart之后(我没试)。在onSaveInstanceState和onRestoreInstanceState方法中,系统自动为我们做了一定的恢复工作。
当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为我们恢复这些数据,比如文本框中用户输入的数据、ListView滚动的位置等。具体针对某一个特定的View系统能为我们恢复哪些数据,我们可以查看View的源码。和Activity一样,每个View都有onSaveInstanceState和onRestoreInstanceState这两个方法,看具体实现就能知道每个View恢复哪些数据。
保存和恢复View层次结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说它很可能是DecorView。最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层、父容器委托子元素去处理一件事情,这种思想在Android中有很多应用,比如View的绘制过程、事件分发等都是采用类似的思想。
TextView# onSaveInstanceState
@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;
}
在onSaveInstanceState中存储一个字符串,然后当Activity被销毁并重新创建后,我们再去获取之前存储的字符串。接收的位置可以选择onRestoreInstanceState或者onCreate,二者的区别是:onRestoreInstanceState一旦被调用,其参数Bundle savedInstanceState一定是有值的,我们不用额外地判断是否为空;但是onCreate不行,onCreate如果是正常启动的话,其参数Bundle savedInstanceState为null,所以必须要额外判断。这两个方法我们选择任意一个都可以进行数据恢复,但是官方文档的建议是采用onRestoreInstanceState去恢复数据。
当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数数据。如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。
当系统配置发生改变后,Activity会被重新创建,那么有没有办法不重新创建呢?答案是有的,接下来我们就来分析这个问题。系统配置中有很多内容,如果当某项内容发生改变后,我们不想系统重新创建Activity,可以给Activity指定configChanges属性。比如不想让Activity在屏幕旋转的时候重新创建,就可以给configChanges属性添加orientation这个值android:configChanges="orientation"
。
系统配置中所含的项目及含义如表所示。
如果我们没有在Activity的configChanges属性中指定该选项的话,当配置发生改变后就会导致Activity重新创建。上面表格中的项目是我们常用的只有locale、orientation和keyboardHidden。
screenSize和smallestScreenSize两个比较特殊,它们的行为和编译选项有关,但和运行环境无关。
<activity
android:name="com.ryg.chapter_1.MainActivity"
android:configChanges="**orientation**|**screenSize**"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.d(TAG, "onConfigurationChanged, newOrientation:" + newConfig.
orientation);
}
由于编译时笔者指定的minSdkVersion和targetSdkVersion大于13,所以为了防止旋转屏幕时Activity重启,除了orientation,我们还要加上screenSize,原因在上面的表格里已经说明了。其他代码还是不变。
由日志可见,Activity的确没有重新创建,并且也没有调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,取而代之的是系统调用了Activity的onConfigurationChanged方法,这个时候我们就可以做一些自己的特殊处理了。
我们在设计程序的时候,我们会说这个界面有什么功能,那个界面有什么功能,多个界面之间如何协调。对于用户来说,他们感知的也是一个个独立的界面。当我们通过通讯软件调用邮箱app的发送邮件界面时,我们喜欢看到的只是发送邮件的界面,而不需要看其他界面具体的调用细节。以功能模块为应用模型的设计从一个主功能模块入口,然后通过用户的输入去调用不同的功能模块。与其类似,android程序也有一个主界面,通过这个主界面,接受用户的操作去调用其他的界面。组成android程序的,是一个个的界面,而每一个界面,对应一个Activity。因此,Activity是android平台应用模型的基本组成成分。
那如何做到每个界面之间彼此解耦、各自的显示不发生混乱、界面之间的跳转有条不紊等等?这些工作,官方都帮我们做好了。Activity就是在这个设计思想下开发出来的。当我们在Activity上开发的时候,就已经沿用了他的这种设计思想。当我们开发一个app的时候,最开始要考虑的,是界面如何设计。设计好界面之后,就是考虑如何开发每个界面了。那我们如何自定义好每一个界面?如何根据我们的需求去设计每个界面的功能?Activity并没有main方法,我们的代码该写在哪里被执行?答案就是:生命周期回调方法。
到这里,你应该可以理解为什么启动activity并不是一句new就可以解决的吧?Activity承担的责任非常多,需要初始化的逻辑也非常多。当Activity被启动,他会根据自身的启动情况,来回调不同的生命周期方法。其中,承担初始化整个界面已经各个功能组件的初始化任务的就是onCreate方法。他有点类似于我们功能模块的入口函数,在这里我们通过setContentView来设计我们界面的布局,通过setOnClickListenner来给每个view设置监听等等。在MVVM设计模式中,还需要初始化viewModel、绑定数据等等。这是生命周期的第一个非常重要的意义所在。
系统不知道你切换到其他地方后要运行多少其他的程序,自然也不知Activity A是否会被销毁,故系统会调用onSaveInstanceState()。
例:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// 通过Bundle参数以键值对的方式进行数据的存储
// 数据恢复:onRestoreInstanceState() & onCreate()
// 上述二者都有一个Bundle类型的参数用于恢复数据
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// ...
super.onSaveInstanceState(savedInstanceState);
}
当系统“未经你许可”时,确实销毁了你的Activity,则重新启动时会被系统调用
若 异常关闭了Activity,即调用了onSaveInstanceState() & 下次启动时会调用onRestoreInstanceState()
注:此时结合Activity的生命周期的调用顺序是:
onCreate()
onStart()
onRestoreInstanceState()
onResume()
例:
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
在api28及以上版本是先onStop再onSaveInstanceState调用的,
但是在低版本中,是先onSaveInstanceState再onStop的。
当activity进入后台的时候,onSaveInstanceState方法则会被调用,而不是异常情况下才会调用onSaveInstanceState方法,因为并不确定在后台时,activity是否会被系统杀死,所以以最保险的方法,先保存数据。当确实是因为异常情况被杀死时,返回activity用户期望界面需要恢复数据,才会调用onRestoreInstanceState
来恢复数据**,其他情况不会触发这个过程**。但activity直接按返回键或者调用finish方法直接结束Activity时,非常明确下一次返回该activity用户期望的是一个干净界面的新activity,就不调用。
onSaveInstanceState不能做重量级的数据存储,其存储数据的原理是把数据序列化到磁盘中,如果存储的数据过于庞大,会导致界面卡顿,掉帧等情况出现。正常情况下,每个view都会重写这两个方法,当activity的这两个方法被调用的时候,会向上委托window去调用顶层viewGroup的这两个方法;而viewGroup会递归调用子view的onSaveInstanceState/onRestoreInstanceState方法,这样所有的view状态就都被恢复了。
onPause调用之后,activity会进入后台。而前台交互的activity只能有一个,所以原activity必须先进入后台后,目标activity才能启动并进入前台。onStop调用之后activity变得不可见,因而只有在目标activity即将要与用户交互的时候,需要进行显示了,原Activity才会调用onStop方法进入不可见状态。
当从后台会到前台时,系统先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,Activity又进入了运行状态。
执行onRestart
当因资源配置改变时,activity会销毁重建,最常见的就是屏幕旋转。这个时候属于异常情况的Activity生命结束。因而,在销毁的时候,会调用onSaveInstanceState来保存数据,在重新创建新的activity的时候,会调用onRestoreInstanceState来恢复数据。
生命周期的一个重要作用就是让activity在不同状态之间切换的时候,可以执行对应的逻辑。举个栗子。我们在界面A使用了相机资源,当我们切换到下个界面B的时候,那么界面A就必须释放相机资源,这样才不会导致界面B无法使用相机;而当我们切回界面A的时候,又希望界面A继续保持拥有相机资源的状态;那么我们就需要在界面不可见的时候释放相机资源,而在界面恢复的时候再次获取相机资源。每个Activity一般情况下可以认为是一个界面或者说,一个屏幕。当我们在界面之间进行导航切换的时候,其实就是在切换Activity。当界面在不同状态之间进行切换的时候,也就是Activity状态的切换,就会回调activity相关的方法。
Android开发艺术探索
https://blog.csdn.net/weixin_43766753/article/details/109565384
https://blog.csdn.net/carson_ho/article/details/107242189
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。