Android之activity与fragment的页面数据短暂保存
知识点:
1、activity/fragment页面数据短期保存状态使用实例;
2、存储流程的onSaveInstanceState(Bundle outState)/onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)源码简析
3、取出流程的onRestoreInstanceState(Bundle savedInstanceState)源码解析;
先上代码,使用实例:
在activity中重写3个方法:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String v1 = null, v2 = null;
if (savedInstanceState != null) {
v1 = savedInstanceState.getString("key1");
v2 = savedInstanceState.getString("key2");
}
LogUtil.logInfo("login:onCreate().......v1 = " + v1);
LogUtil.logInfo("login:onCreate().......v2 = " + v2);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
LogUtil.logInfo("login:onSaveInstanceState(1).......");
outState.putString("key1", "value1");
/* 点击进去看源码 */
super.onSaveInstanceState(outState);
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
LogUtil.logInfo("login:onSaveInstanceState(2).......");
outState.putString("key2", "value2");
super.onSaveInstanceState(outState, outPersistentState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
String value1 = null, value2 = null;
if (savedInstanceState != null) {
value1 = savedInstanceState.getString("key1");
value2 = savedInstanceState.getString("key2");
}
LogUtil.logInfo("login:onRestoreInstanceState(1).......value1 = " + value1);
LogUtil.logInfo("login:onRestoreInstanceState(2).......value2 = " + value2);
super.onRestoreInstanceState(savedInstanceState);
}
动作:先启动此页面,然后按home键,然后在进入此页面,打印如下:
03-08 17:41:50.652 12113-12113/? I/-=-=>>: login:onCreate().......v1 = null
03-08 17:41:50.652 12113-12113/? I/-=-=>>: login:onCreate().......v2 = null
03-08 17:41:55.329 12113-12113/com.yaojt I/-=-=>>: login:onSaveInstanceState(1).......
分析:可以看到,onCreate()时时没有赋值的,然后点击HOME键的时候,走了onSaveInstanceState(Bundle outState)这个方法,保存了数据“value2”,但是没有走onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)方法;在回到这个页面,我们看到并没有走onRestoreInstanceState(Bundle savedInstanceState)方法,那是因为我们离开此activity的时间比较短,此activity并没有因为内存不足或者资源不足而被系统“非法杀掉”(不是程序主动杀掉),所以不用帮我们保存页面各个状态。如果真的是被系统“非法杀掉”,那么系统就有义务帮我们保存此页面的状态数据,因为这个不是程序的意愿,所以系统才会调用onSaveInstanceState(Bundle outState)方法。
记住这里,系统只是给了我们一个短期保存状态的选项,做不做保存状态就看程序的需求了。注意,这个是不能长期给你保存的。
保存的流程(附源码简析):
/*
首先是在activity/fragment里头调用重写下面方法,在bundle里头保存需要保存的数据
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
LogUtil.logInfo("login:onSaveInstanceState().......");
outState.putString("key", "value");
/* 点击进去看源码 */
super.onSaveInstanceState(outState);
}
然后到这个方法:
/*这个方法只有一句注释:保存所有正确的fragment的状态
在调用以上方法之前,还会检查是不是有需要在fragment进行分发的所有result,这些是由Fragment.startActivityForResult(…)启动而返回的结果。最多能有2^16-1个result。
mPendingFragmentActivityResults分别保存了key-value
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
if (mPendingFragmentActivityResults.size() > 0) {
outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);
int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
}
outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
}
}
再到这个方法:
/*
方法解释:在activity被系统杀掉之前,此方法会被调用用来保存先前实例的状态,然后在onCreate()或者onRestoreInstanceState()方法都可以获得先前保存的实例的各个状态。
此方法在一个activity可能被杀掉的时候会调用,当在一段时间内回到此页面,就可以重新获得保存的各个状态。例如,在用户启动了activity B,而此时activity A 页面可能因为资源紧张而被杀掉,那么activity A就有机会当前用户界面的各个状态,然后可以再回到activity A 的时候,那些用户的状态就可以重新在onCreate()或者onRestoreInstanceState()方法取出来。
注:后面的就不一一对应翻译了,说个主要内容
这里有一点需要明白:关于activity/fragment的生命周期和onSaveInstanceState()的关系;当用户例如点击了back按键,那么这个页面一定会被销毁的,会调用ondestore()方法,下一次用户再进来这个页面,就一定会重新创建这个页面。如果在调用onSaveInstanceState()方法,“短期”保存这些状态也就没意义了,因为页面一定会重新创建。
默认地,系统会帮你保存view页面的层级关系(具有id的控件)和焦点的控件。一般来说,我们都需要调用父类的方法,让系统默认保存以上的信息。是下面这句代码:outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());保存层级关系控件。
此方法会在onstop()方法之前调用,不能保证在onPause()之前或者之后调用。
*/
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);
}
取出保存的数据流程:
/*
首先调用此方法,获得在onSaveInstanceState()保存的各个数据
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
String value = null;
if (savedInstanceState != null) {
value = savedInstanceState.getString("key");
}
LogUtil.logInfo("login:onRestoreInstanceState().......value = " + value);
super.onRestoreInstanceState(savedInstanceState);
}
super.onRestoreInstanceState(savedInstanceState)调用的是以下的方法
/*
当此activity重新被初始化时,此方法可以获取到从在savedInstanceState()方法里头保存的数据,用来初始化此activity。最简单的用法是在onCreate()获取到存储的状态。但是,在所有的初始化动作完成后做此操作,或者为了允许你的子类去决定是不是使用你自己保存的默认数据的时候,在这个方法进行处理是更加方便的,Google推荐。
在此方法中,系统会默认帮你还原之前view的所有状态。
*/
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
我们在这里还看到一个重载的保存状态的方法,抱有好奇心,再进去看看源码:
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
LogUtil.logInfo("login:onSaveInstanceState().......");
outState.putString("key", "value");
super.onSaveInstanceState(outState, outPersistentState);
}
进入源码去看看,看到这里
/**
* This is the same as {@link #onSaveInstanceState} but is called for activities
* created with the attribute {@link android.R.attr#persistableMode} set to
* <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
* in will be saved and presented in {@link #onCreate(Bundle, PersistableBundle)}
* the first time that this activity is restarted following the next device reboot.
*
* @param outState Bundle in which to place your saved state.
* @param outPersistentState State which will be saved across reboots.
*
* @see #onSaveInstanceState(Bundle)
* @see #onCreate
* @see #onRestoreInstanceState(Bundle, PersistableBundle)
* @see #onPause
*/
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
onSaveInstanceState(outState);
}
这里大概的意思是说:此方法和onSaveInstanceState()是一样的,区别是,此方法是为activity在有属性persistableMode(用来给persistAcrossReboots设值)时创建而调用。PersistableBundle会被保存和传递到onCreate(Bundle, PersistableBundle)的第二个参数中,在下一次设备重启时,会被调用onCreate(Bundle, PersistableBundle)方法。
追踪源码,最后看到他也是调用了Activity类的onSaveInstanceState(Bundle outState)方法。
总结:其实我们只需要知道怎么用就可以了,毕竟这个也是不难的使用方法。但是源码还是有必要去看看的,源码也不难;
如有任何疑问,请及时与我联系,谢谢!