背景:当转屏幕时,activity的页面中封装一个成员变量a,每次转屏幕时,自加一,在onSaveInstanceState()方法中实现逻辑代码,并在onCreate()方法中用EditText控件显示递增的成员变量a,但是出现EditText无法显示的问题
问题源码如下:
问题的解决:
- 安卓会自动保存某些view的状态,旋转屏幕->onSaveInstanceState保存了EditText的内容->然后在onCreate的时候其实已经给EditText设置成功了,但是随后会在->onRestoreInstanceState尝试恢复之前的值,所以造成了值没改变。saveEnabled设为false就不会恢复这个EditText的值。
- 源码断点分析
- {android:viewHierarchyState=Bundle[{android:Panels=android.util.SparseArray@4130d260, android:focusedViewId=2131034172, android:views=android.util.SparseArray@412fe3a0, android:ActionBar=android.util.SparseArray@412f21d8}],a=1}
- 追根到底[android.view.AbsSavedState$1@4120ae10, TextView.SavedState{412fe658 start=0 end=0 text=第一次启动}, null, null, null, null, null, null, null, null, null, null, null]
问题:每次当android手机转屏幕时,当前的activity会自动销毁与重建,那么在调用的生命周期的过程是怎么样的呢?
手机操作:
-
第一次启动
- 修改EditText文字内容然后再转屏幕
问题:那么如何从源码里解释呢? + 解决这个问题之前,我自己设想如果是我,我怎么编?
-
这里的应用场景是什么:
如果因为系统资源紧张而导致Activity的Destory, 系统会在用户回到这个Activity时有这个Activity存在过的记录,系统会使用那些保存的记录数据(描述了当Activity被Destory时的状态)来重新创建一个新的Activity实例。那些被系统用来恢复之前状态而保存的数据被叫做 "instance state" ,它是一些存放在Bundle对象中的key-value pairs。
-
实例
Caution:你的Activity会在每次旋转屏幕时被destroyed与recreated。当屏幕改变方向时,系统会Destory与Recreate前台的activity,因为屏幕配置被改变,你的Activity可能需要加载一些alternative的资源(例如layout).
-
特点:
- 我们不想Activity被结束,但是因为某些原因(释放内存资源等)系统会自动Kill了,这个不是我们能控制的,是系统级控制的,但是APP进程未被终结。
- 我们仍然需要这个Activity对象和原来保存的状态,所以需要某个地方能保存这个对象或原来对象的状态
- 再次回到Acitivity页面时,我希望能再读取原来的状态并恢复
- 想想在这个过程中,我们可能需要什么?
- 如果这是系统级控制,那么系统必须有一个类或者有个模块专门负责系统级事件的分发给指定的处理方法,例如将转屏幕,语言配置改变等事件分发给void xxxA(){};
- 在销毁Activity时,我们要得到这个页面的引用,并遍历该页面,知道有哪些需要保存并且将来恢复页面时需要恢复的,并保存在一个安全的地方,不会跟着对象的销毁而销毁。
- 整理转屏幕这个事件发生的的流程
- 转屏幕事件发生,系统监听到,将消息分发到相应的处理器
- 处理器调用相关方法,这里我们猜一下发生了什么
- 系统先后调用onPause()方法、onSaveInstanceState()方法、onStop()、onDestroy()、onCreate()、onStrat()、onRestoreInstanceState()、onResume().(以上各方法定为方一)
- 方一让页面暂停业务逻辑,方二保存所需对象的状态,方三释放某些资源,方四开始销毁页面,方五开始再次创建所需对象、方七开始获得原来对象的状态,方八开始恢复业务逻辑
- 现在我们带着疑问去找源码
- 系统监听到事件是在哪里?
- onSaveInstanceState()做了什么事?
源码
protected void onSaveInstanceState(Bundle outState) { outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());//封装当前窗口布局的层级结构 Parcelable p = mFragments.saveAllState();//这里保存了所有的状态 if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p);//??? } getApplication().dispatchActivitySaveInstanceState(this, outState);//将该页面也传递的outState绑定在一起 }
-
protected void onRestoreInstanceState(Bundle savedInstanceState) { if (mWindow != null) {//当前窗口存在 Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);//找到之前保存的Bundle对象 if (windowState != null) { mWindow.restoreHierarchyState(windowState);//恢复之前的状态 } } }
-基于View的不同组件重写View的getDefauleEditable()方法,使其在上述情况时,是否应该要自动保存内容。EditText组件在转屏幕时没有显示出原来的效果就是因为以下代码
protected boolean getDefaultEditable() { return false; }
PS:还有一些限于自身能力,还无法解决。但假以时日,必能明白其中原理。