1.备忘录模式的定义及使用场景
备忘录模式是一种行为模式,该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态。备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问,目的是为了保护好被保存的这些对象状态的完整性以及内部实现不向外暴露。
定义:
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可将该对象回复到原先保存的状态
使用场景:
- 需要保存一个对象在某一个时刻的状态或部分状态
- 需要用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态
2. 备忘录模式的优缺点
2.1优点
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
- 实现了信息的封装,使得用户不需要关心状态的保持细节
2.2缺点
消耗资源,如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
3.注意事项
- 备忘录的生命期
备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理 - 备忘录的性能
不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),主要原因是一是控制不了备忘录建立的对象数量;而是大对象的建立是要消耗资源的,系统的性能需要考虑。
4. 备忘录模式的实现方式
public class Memento {
//发起人的内部状态
private String state="";
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public class Caretaker {
//备忘录对象
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
public class Originator {
//内部状态
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//创建一个备忘录
public Memento createMemento() {
return new Memento(state);
}
//恢复一个备忘录
public void restoreMemento(Memento memento) {
this.setState(memento.getState());
}
}
public class Test {
public static void main(String args[]) {
//定义出发起人
Originator originator = new Originator();
originator.setState("初始:1111111111111");
//定义出备忘录管理员
Caretaker caretaker = new Caretaker();
System.out.println(originator.getState());
//创建一个备忘录
caretaker.setMemento(originator.createMemento());
originator.setState("改变:22222222222222");
System.out.println(originator.getState());
originator.setState("恢复:restoreMemento");
originator.restoreMemento(caretaker.getMemento());
System.out.println(originator.getState());
}
}
5. 备忘录模式在Android中的实际应用
在Android开发中,状态模式应用是Android中的状态保持,也就是里面的onSaveInstanceState和onRestoreInstanceState。当Activity不是正常方式退出,且Activity在随后的时间内被系统杀死之前会调用这两个方法让开发人员可以有机会存储Activity的相关信息,并且在下次放好Activity的时候恢复这些数据。
Activity:
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState()); //保存当前窗口的视图树的状态
Parcelable p = mFragments.saveAllState(); //存储Fragment的状态
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
Window的实现为PhoneWindow:
/** {@inheritDoc} */
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
//通过SparseArray类来存储,这相当于一个key为整型的map
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
//mContentParent就是调用Activity的setContentView函数设置的内容视图,它是内容视图的根节点,在这里存储整棵树的结构
mContentParent.saveHierarchyState(states);
//将视图树结构放到outState中
outState.putSparseParcelableArray(VIEWS_TAG, states);
// 保存当前界面中获取了焦点的View
View focusedView = mContentParent.findFocus();
if (focusedView != null) {
if (focusedView.getId() != View.NO_ID) {
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
} else {
if (false) {
Log.d(TAG, "couldn't save which view has focus because the focused view "
+ focusedView + " has no id.");
}
}
}
// 存储整个面板的状态
SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
savePanelState(panelStates);
if (panelStates.size() > 0) {
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
//存储actionBar的状态
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}
在saveHierarchyState函数中,主要是存储了与当前UI、ActionBar相关的View状态。mContentParent就是我们通过Activity的setContentView函数设置的内容视图,他是这个内容视图的根节点。mContentParent是一个ViewGroup对象,但是saveHierachyState是在父类View中
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
container.put(mID, state); //将自身状态放到container中 key 为id value为自身状态
}
}
}
//View类默认的存储的状态为空
protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (mStartActivityRequestWho != null) {
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
return state;
}
return BaseSavedState.EMPTY_STATE;
}
恢复数据的调用过程如下,基本流程与保存类似
Activity:
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
PhoneWindow:
/** {@inheritDoc} */
@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
if (mContentParent == null) {
return;
}
SparseArray<Parcelable> savedStates
= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {
mContentParent.restoreHierarchyState(savedStates);
}
// restore the focused view
int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
if (focusedViewId != View.NO_ID) {
View needsFocus = mContentParent.findViewById(focusedViewId);
if (needsFocus != null) {
needsFocus.requestFocus();
} else {
Log.w(TAG,
"Previously focused view reported id " + focusedViewId
+ " during save, but can't be found during restore.");
}
}
// restore the panels
SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
if (panelStates != null) {
restorePanelState(panelStates);
}
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates =
savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
if (actionBarStates != null) {
doPendingInvalidatePanelMenu();
mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);
} else {
Log.w(TAG, "Missing saved instance states for action bar views! " +
"State will not be restored.");
}
}
}
View:
public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
}
/**
* Called by {@link #restoreHierarchyState(android.util.SparseArray)} to retrieve the
* state for this view and its children. May be overridden to modify how restoring
* happens to a view's children; for example, some views may want to not store state
* for their children.
*
* @param container The SparseArray which holds previously saved state.
*
* @see #dispatchSaveInstanceState(android.util.SparseArray)
* @see #restoreHierarchyState(android.util.SparseArray)
* @see #onRestoreInstanceState(android.os.Parcelable)
*/
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
Parcelable state = container.get(mID);
if (state != null) {
// Log.i("View", "Restoreing #" + Integer.toHexString(mID)
// + ": " + state);
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
onRestoreInstanceState(state);
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onRestoreInstanceState()");
}
}
}
}
protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (state != null && !(state instanceof AbsSavedState)) {
throw new IllegalArgumentException("Wrong state class, expecting View State but "
+ "received " + state.getClass().toString() + " instead. This usually happens "
+ "when two views of different type have the same id in the same hierarchy. "
+ "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
+ "other views do not use the same id.");
}
if (state != null && state instanceof BaseSavedState) {
mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
}
}
在这个过程中,Activity扮演了Caretaker角色,负责存储、恢复UI的状态信息;Activity、Fragement、View、ViewGroup等对象为Originator角色,也就是需要存储状态的对象;Memento则是由Bundle类扮演。
Activity在停止之前会根据Activity的退出情景来选择是否需要存储状态,在重新启动该Activity时会判断ActivityClientRecord对象中是否存储了Activity的状态,如果含有状态则调用Activity的onRestoreInstanceState函数,从而使得Activity的UI效果与上次保持一致,这样一来,就保证了在非正常退出Activity时不会丢失数据的情况,很好地提升了用户体验。