Activity全解(一)
Activity是android的四大组件之一,它的使用频率是最高的,因此想要了解android开发,那么必须要了解Activity。
Activity的生命周期全面解析
一、典型情况下的生命周期
正常情况下,Activity会经历如下声明周期。
1. onCreate():Activity的第一个生命周期,主要是通过setContentView()加载布局。
2. onStart():表示Activity已经被启动了,但是还没有到前台,无法与用户进行交互。
3. onResume():表示Activity已经到了前台,开始活动了,可以与用户交互
4. onPause():表示Activity正在停止。此时可以做一些保存数据、停止动画等操作,但是不能太耗时。
5. onStop():表示Activity即将停止,可以做一些稍微重量级的回收工作,但是不能太耗时
6. onDestroy():Activity的最后一个生命周期,主要做一些回收和释放工作。
7. onRestart():这是一个特殊的生命周期,当用户按下home键或者被覆盖之后,重新将Activity显示出来的时候,执行onRestart()->onStart()->onResume()
二、异常情况下的生命周期
异常情况下是指Activity被系统回收或者是当前设备的Configuration发生改变从而导致Activity被销毁重建。
情况一:资源相关的系统配置发生改变造成的Activity的销毁与重建
图2 异常 情况下的Activity的重建过程
当系统配置发生改变后,Activity会被销毁,onPause、onStop、onDestroy都会被调用,同时会调用onSaveInstanceState来保存当前Activity状态,这个方法执行在onStop()之前,它和onPause()没有既定的时序关系。当Activity被重现的时候调用onRestoreInstanceState()恢复原先状态。它执行在onStart()之后。
例如:
布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="cn.centran.zx_mylock.MainActivity"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/et"/>RelativeLayout>
MainActivity:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et = (EditText) findViewById(R.id.et); }
在Editext中输入文字后,切换到横屏,那么,Activity重建之后,输入的文字也恢复了,大家可以试试。
原因:
查看Editext源码中的onSaveInstanceState()方法:
@Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); // Save state if we are forced to final boolean freezesText = getFreezesText(); boolean hasSelection = false; int start = -1; int end = -1; if (mText != null) { start = getSelectionStart(); end = getSelectionEnd(); if (start >= 0 || end >= 0) { // Or save state if there is a selection hasSelection = true; } } if (freezesText || hasSelection) { SavedState ss = new SavedState(superState); if (freezesText) { if (mText instanceof Spanned) { final Spannable sp = new SpannableStringBuilder(mText); if (mEditor != null) { removeMisspelledSpans(sp); sp.removeSpan(mEditor.mSuggestionRangeSpan); } ss.text = sp; } else { ss.text = mText.toString(); } } if (hasSelection) { // XXX Should also save the current scroll position! ss.selStart = start; ss.selEnd = end; } if (isFocused() && start >= 0 && end >= 0) { ss.frozenWithFocus = true; } ss.error = getError(); if (mEditor != null) { ss.editorState = mEditor.saveInstanceState(); } return ss; } return superState; }
onRestoreInstanceState():
@Override public void onRestoreInstanceState(Parcelable state) { if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState)state; super.onRestoreInstanceState(ss.getSuperState()); // XXX restore buffer type too, as well as lots of other stuff if (ss.text != null) { setText(ss.text); } if (ss.selStart >= 0 && ss.selEnd >= 0) { if (mText instanceof Spannable) { int len = mText.length(); if (ss.selStart > len || ss.selEnd > len) { String restored = ""; if (ss.text != null) { restored = "(restored) "; } Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd + " out of range for " + restored + "text " + mText); } else { Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd); if (ss.frozenWithFocus) { createEditorIfNeeded(); mEditor.mFrozenWithFocus = true; } } } } if (ss.error != null) { final CharSequence error = ss.error; // Display the error later, after the first layout pass post(new Runnable() { public void run() { if (mEditor == null || !mEditor.mErrorWasChanged) { setError(error); } } }); } if (ss.editorState != null) { createEditorIfNeeded(); mEditor.restoreInstanceState(ss.editorState); } }
不需要细看,大致知道当输入数据之后,切换屏幕,那么因为系统配置发生变化,导致该Activity被杀死,但是在onStop()方法之前调用了Editext的onSaveInstanceState()方法,保存了数据,之后重建的时候有调用onRestoreInstanceState()恢复了。
关于保存和恢复View层次结构,系统的工作流程是这样的:首先Activity被意外结束的时候,Activity会嗲用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着window在委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说,它可能是DecorView。最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。这是一种典型的委托思想。
情况二:内存不足,造成低优先级的Activity被杀死
Activity按照优先级从高到低,可以分为如下三种:
- 前台Activity——正在和用户交互的Activity,它的优先级最高
- 可见但非前台Activity——比如Activity中弹出一个对话框,或者打开一个透明的Activity,导致Activity可见,但是位于后台无法和用户直接交互。
后台Activity——已经被暂停的Activity,比如执行了onStop(),优先级最低。
当系统内存不足的时候,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstance和onRestoreInstanceState来存储和恢复数据。
如果一个进程中没有四大组件在执行,那么这个进程将会很快被系统杀死,因此后台工作不适合脱离四大组件单独存在,比较好的方法就是将这些工作放在service中从而保证进程具有一定的优先级,这样不容易被杀死。
如果当系统配置发生改变后,Activity不想被销毁重建,那么应该就在资源配置文件中给Activity制定configChanges属性。
android:configChanges="orientation"
如果想要指定多个值,可以用“|”链接起来,比如:
android:configChanges="orientation|keyboardHidden"
当Activity如果没有被销毁重建,就不会执行onSaveInstanceState和onRestoreInstanceState方法,但是会执行onConfigurationChanged方法。
configChanges的属性含义:
locale:设备的位置发生改变,一般指切换系统语言。
orientation:屏幕方向发生改变,这个是最常用的,比如旋转手机屏幕
keyboardHidden:键盘的可访问性发生了改变,比如用户调出了键盘
screenSize:当屏幕的尺寸信息发生了改变,当旋转设备屏幕时,屏幕尺寸会发生改变,这个选项比较特殊,他和编译选项有关,当编译选项中的minSdkVersion和targetSdkVersion均低于13时,此选项不会导致Activity重启,否则就会。
smallestScreenSize:与screenSize类似,只是当minSdkVersion和targetSdkVersion有小于13时,Activity会重启,否则不会。
更多相关文章
- Android之碎片Fragment的生命周期
- 阿里云面试(android)
- android:configChanges="keyboardHidden|orientation"配置
- Android动画-Interpolator(插值器)大全
- Android探索:全面分析Activity的生命周期&IntentFilter匹配规则
- Android(安卓)TabLayout简单使用
- Android学习之旅(三)----Activity及其生命周期
- android onTrimMemory()和onLowMemory()
- 如何让手机横竖屏切换不重走Activity生命周期