您的位置:首页 > 移动开发 > Android开发

Android BaseSavedState 以及 Activity onSaveInstanceState 和 onRestoreInstanceState 方法使用注意事项

2014-03-07 17:03 706 查看
下面的代码来自View这个类,是一个关于状态保存的类,我们浏览器用与崩溃恢复,很有用的哦,我还看到过同事用来做View的状态保存,反正很强大的说~
/**
* Base class for derived classes that want to save and restore their own
* state in {@link android.view.View#onSaveInstanceState()}.
*/
public static class BaseSavedState extends AbsSavedState {
/**
* Constructor used when reading from a parcel. Reads the state of the superclass.
*
* @param source
*/
public BaseSavedState(Parcel source) {
super(source);
}

/**
* Constructor called by derived classes when creating their SavedState objects
*
* @param superState The state of the superclass of this view
*/
public BaseSavedState(Parcelable superState) {
super(superState);
}

public static final Parcelable.Creator<BaseSavedState> CREATOR =
new Parcelable.Creator<BaseSavedState>() {
public BaseSavedState createFromParcel(Parcel in) {
return new BaseSavedState(in);
}

public BaseSavedState[] newArray(int size) {
return new BaseSavedState[size];
}
};
}

相关的有Activity的onSaveInstanceState,onRestoreInstanceState:

/**
* Called to retrieve per-instance state from an activity before being killed
* so that the state can be restored in {@link #onCreate} or
* {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
* will be passed to both).
*
* <p>This method is called before an activity may be killed so that when it
* comes back some time in the future it can restore its state.  For example,
* if activity B is launched in front of activity A, and at some point activity
* A is killed to reclaim resources, activity A will have a chance to save the
* current state of its user interface via this method so that when the user
* returns to activity A, the state of the user interface can be restored
* via {@link #onCreate} or {@link #onRestoreInstanceState}.
*
* <p>Do not confuse this method with activity lifecycle callbacks such as
* {@link #onPause}, which is always called when an activity is being placed
* in the background or on its way to destruction, or {@link #onStop} which
* is called before destruction.  One example of when {@link #onPause} and
* {@link #onStop} is called and not this method is when a user navigates back
* from activity B to activity A: there is no need to call {@link #onSaveInstanceState}
* on B because that particular instance will never be restored, so the
* system avoids calling it.  An example when {@link #onPause} is called and
* not {@link #onSaveInstanceState} is when activity B is launched in front of activity A:
* the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't
* killed during the lifetime of B since the state of the user interface of
* A will stay intact.
*
* <p>The default implementation takes care of most of the UI per-instance
* state for you by calling {@link android.view.View#onSaveInstanceState()} on each
* view in the hierarchy that has an id, and by saving the id of the currently
* focused view (all of which is restored by the default implementation of
* {@link #onRestoreInstanceState}).  If you override this method to save additional
* information not captured by each individual view, you will likely want to
* call through to the default implementation, otherwise be prepared to save
* all of the state of each view yourself.
*
* <p>If called, this method will occur before {@link #onStop}.  There are
* no guarantees about whether it will occur before or after {@link #onPause}.
*
* @param outState Bundle in which to place your saved state.
*
* @see #onCreate
* @see #onRestoreInstanceState
* @see #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);
}


/**
* This method is called after {@link #onStart} when the activity is
* being re-initialized from a previously saved state, given here in
* <var>savedInstanceState</var>.  Most implementations will simply use {@link #onCreate}
* to restore their state, but it is sometimes convenient to do it here
* after all of the initialization has been done or to allow subclasses to
* decide whether to use your default implementation.  The default
* implementation of this method performs a restore of any view state that
* had previously been frozen by {@link #onSaveInstanceState}.
*
* <p>This method is called between {@link #onStart} and
* {@link #onPostCreate}.
*
* @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
*
* @see #onCreate
* @see #onPostCreate
* @see #onResume
* @see #onSaveInstanceState
*/
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}


这些都和状态保存相关系的~值得深入理解一下

比如在HorizontalScrollView 中的最后一段代码,就用来保存View的状态:

static class SavedState extends BaseSavedState {
public int scrollPosition;
public boolean isLayoutRtl;

SavedState(Parcelable superState) {
super(superState);
}

public SavedState(Parcel source) {
super(source);
scrollPosition = source.readInt();
isLayoutRtl = (source.readInt() == 0) ? true : false;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(scrollPosition);
dest.writeInt(isLayoutRtl ? 1 : 0);
}

@Override
public String toString() {
return "HorizontalScrollView.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " scrollPosition=" + scrollPosition
+ " isLayoutRtl=" + isLayoutRtl + "}";
}

public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}

public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}

 
http://blog.csdn.net/murongshusheng/article/details/8199538
这里总结了很多细节,我之前就疏忽了为什么按back键就不保存状态呢?原来不是什么时候都保存状态的。

1.在一个activity被销毁前,不一定会调用
onSaveInstanceState()
这个方法,因为不是所有情况都需要去存储activity的状态(例如当用户按回退键退出你的activity的时候,因为用户指定关掉这个activity)。

2.如果这个方法被调用,它一定会在 
onStop()
方法之前,可能会在onPause()方法之前。

3.布局中的每一个View默认实现了
onSaveInstanceState()
方法,这样的话,这个UI的任何改变都会自动的存储和在activity重新创建的时候自动的恢复。但是这种情况只有在你为这个UI提供了唯一的ID之后才起作用,如果没有提供ID,将不会存储它的状态。

4.由于默认的
onSaveInstanceState()
方法的实现帮助UI存储它的状态,所以如果你需要覆盖这个方法去存储额外的状态信息时,你应该在执行任何代码之前都调用父类的
onSaveInstanceState()
方法(
super.onSaveInstanceState()
)。

5.由于
onSaveInstanceState()
方法调用的不确定性,你应该只使用这个方法去记录activity的瞬间状态(UI的状态)。不应该用这个方法去存储持久化数据。当用户离开这个activity的时候应该在
onPause()
方法中存储持久化数据(例如应该被存储到数据库中的数据)。

文章链接:http://blog.csdn.net/murongshusheng/article/details/8199538

实例,写的很棒:是一个自定义View SaveState的例子,非常好。
http://disanji.net/2011/04/28/android-view-onsaveinstancestate-onrestoreinstancestate/ 


后面主要写写Activity onSaveInstanceState 和 onRestoreInstanceState 方法使用注意事项,我觉得这个地方非常重要:

在Activity中,有两个方法用于临时保存、恢复状态信息,注意是临时信息,如果你关机重启肯定保存不了。这两个方法是:
public void onSaveInstanceState(Bundle savedInstanceState);
public void onRestoreInstanceState(Bundle savedInstanceState);  
 
在写Demo测试Activity生命周期的时候,发现onRestoreInstanceState方法并不会总是被调用,上网查了一下,这两个方法主要用于在切换屏幕布局时保存APP数据的,文章中提到“如果我们的Activity在后台没有因为运行内存吃紧被清理,则切换回时会触发onRestoreInstanceState方法”。因为内存回收不受我们控制,所以在除了在切换屏幕保存数据状态以外,最好使用onPause方法和onResume方法来保存和读取程序状态。

以下几种情况会调用onSaveInstanceState,分别是:
用户按下HOME键;
长按HOME键,选择运行其他的程序;
按下电源按键;
屏幕方向切换(可能);
启动新的Activity等;

适合临时信息:当要保存持久信息时,不要使用这个保存方法!举个简单的例子,当我们点击Home后,onSaveInstanceState方法会被调用,此时,如果从Settings里面终止程序,那么,回来后发现保存的数据是不存在的。

至于这两个函数的使用,给出示范代码(留意自定义代码在调用super的前或后):

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
super.onSaveInstanceState(savedInstanceState);
}

@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");
}


参考:(下面的链接都非常有价值)
http://blog.csdn.net/ddna/article/details/5123482 http://blog.csdn.net/spare_h/article/details/6659010 http://android.blog.51cto.com/268543/634646/ http://www.th7.cn/Program/Android/201312/162981.shtml http://www.charlesharley.com/2012/programming/views-saving-instance-state-in-android/ http://stackoverflow.com/questions/3542333/how-to-prevent-custom-views-from-losing-state-across-screen-orientation-changes http://stackoverflow.com/questions/14891434/overriding-view-onsaveinstancestate-and-view-onrestoreinstancestate-using-vi https://github.com/CharlesHarley/Example-Android-SavingInstanceState/blob/master/src/com/example/android/savinginstancestate/views/LockCombinationPicker.java
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: