1.備忘錄模式的定義及使用場(chǎng)景
備忘錄模式是一種行為模式,該模式用于保存對(duì)象當(dāng)前狀態(tài),并且在之后可以再次恢復(fù)到此狀態(tài)。備忘錄模式實(shí)現(xiàn)的方式需要保證被保存的對(duì)象狀態(tài)不能被對(duì)象從外部訪問(wèn),目的是為了保護(hù)好被保存的這些對(duì)象狀態(tài)的完整性以及內(nèi)部實(shí)現(xiàn)不向外暴露。
定義:
在不破壞封閉的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣,以后就可將該對(duì)象回復(fù)到原先保存的狀態(tài)
使用場(chǎng)景:
需要保存一個(gè)對(duì)象在某一個(gè)時(shí)刻的狀態(tài)或部分狀態(tài)
需要用一個(gè)接口來(lái)讓其他對(duì)象得到這些狀態(tài),將會(huì)暴露對(duì)象的實(shí)現(xiàn)細(xì)節(jié)并破壞對(duì)象的封裝性,一個(gè)對(duì)象不希望外界直接訪問(wèn)其內(nèi)部狀態(tài),通過(guò)中間對(duì)象可以間接訪問(wèn)其內(nèi)部狀態(tài)

2. 備忘錄模式的優(yōu)缺點(diǎn)
2.1優(yōu)點(diǎn)
給用戶提供了一種可以恢復(fù)狀態(tài)的機(jī)制,可以使用戶能夠比較方便地回到某個(gè)歷史的狀態(tài)
實(shí)現(xiàn)了信息的封裝,使得用戶不需要關(guān)心狀態(tài)的保持細(xì)節(jié)
2.2缺點(diǎn)
消耗資源,如果類的成員變量過(guò)多,勢(shì)必會(huì)占用比較大的資源,而且每一次保存都會(huì)消耗一定的內(nèi)存
3.注意事項(xiàng)
備忘錄的生命期備忘錄創(chuàng)建出來(lái)就要在“最近”的代碼中使用,要主動(dòng)管理它的生命周期,建立就要使用,不使用就要立刻刪除其引用,等待垃圾回收器對(duì)它的回收處理
備忘錄的性能不要在頻繁建立備份的場(chǎng)景中使用備忘錄模式(比如一個(gè)for循環(huán)中),主要原因是一是控制不了備忘錄建立的對(duì)象數(shù)量;而是大對(duì)象的建立是要消耗資源的,系統(tǒng)的性能需要考慮。
4. 備忘錄模式的實(shí)現(xiàn)方式
public class Memento {
//發(fā)起人的內(nèi)部狀態(tài)
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 {
//備忘錄對(duì)象
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}```
public class Originator {
//內(nèi)部狀態(tài)
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//創(chuàng)建一個(gè)備忘錄
public Memento createMemento() {
return new Memento(state);
}
//恢復(fù)一個(gè)備忘錄
public void restoreMemento(Memento memento) {
this.setState(memento.getState());
}
}```
public class Test {
public static void main(String args[]) {
//定義出發(fā)起人
Originator originator = new Originator();
originator.setState("初始:1111111111111");
//定義出備忘錄管理員
Caretaker caretaker = new Caretaker();
System.out.println(originator.getState());
//創(chuàng)建一個(gè)備忘錄
caretaker.setMemento(originator.createMemento());
originator.setState("改變:22222222222222");
System.out.println(originator.getState());
originator.setState("恢復(fù):restoreMemento");
originator.restoreMemento(caretaker.getMemento());
System.out.println(originator.getState());
}
}```
5. 備忘錄模式在Android中的實(shí)際應(yīng)用
在Android開(kāi)發(fā)中,狀態(tài)模式應(yīng)用是Android中的狀態(tài)保持,也就是里面的onSaveInstanceState和onRestoreInstanceState。當(dāng)Activity不是正常方式退出,且Activity在隨后的時(shí)間內(nèi)被系統(tǒng)殺死之前會(huì)調(diào)用這兩個(gè)方法讓開(kāi)發(fā)人員可以有機(jī)會(huì)存儲(chǔ)Activity的相關(guān)信息,并且在下次放好Activity的時(shí)候恢復(fù)這些數(shù)據(jù)。
Activity:
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState()); //保存當(dāng)前窗口的視圖樹(shù)的狀態(tài)
Parcelable p = mFragments.saveAllState(); //存儲(chǔ)Fragment的狀態(tài)
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}```
Window的實(shí)現(xiàn)為PhoneWindow:
/** {@inheritDoc} */
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
//通過(guò)SparseArray類來(lái)存儲(chǔ),這相當(dāng)于一個(gè)key為整型的map
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
//mContentParent就是調(diào)用Activity的setContentView函數(shù)設(shè)置的內(nèi)容視圖,它是內(nèi)容視圖的根節(jié)點(diǎn),在這里存儲(chǔ)整棵樹(shù)的結(jié)構(gòu)
mContentParent.saveHierarchyState(states);
//將視圖樹(shù)結(jié)構(gòu)放到outState中
outState.putSparseParcelableArray(VIEWS_TAG, states);
// 保存當(dāng)前界面中獲取了焦點(diǎn)的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.");
}
}
}
// 存儲(chǔ)整個(gè)面板的狀態(tài)
SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
savePanelState(panelStates);
if (panelStates.size() > 0) {
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
//存儲(chǔ)actionBar的狀態(tài)
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}```
在saveHierarchyState函數(shù)中,主要是存儲(chǔ)了與當(dāng)前UI、ActionBar相關(guān)的View狀態(tài)。mContentParent就是我們通過(guò)Activity的setContentView函數(shù)設(shè)置的內(nèi)容視圖,他是這個(gè)內(nèi)容視圖的根節(jié)點(diǎn)。mContentParent是一個(gè)ViewGroup對(duì)象,但是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); //將自身狀態(tài)放到container中 key 為id value為自身狀態(tài)
}
}
}```
//View類默認(rèn)的存儲(chǔ)的狀態(tài)為空
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;
}```
恢復(fù)數(shù)據(jù)的調(diào)用過(guò)程如下,基本流程與保存類似
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;
}
}```
在這個(gè)過(guò)程中,Activity扮演了Caretaker角色,負(fù)責(zé)存儲(chǔ)、恢復(fù)UI的狀態(tài)信息;Activity、Fragement、View、ViewGroup等對(duì)象為Originator角色,也就是需要存儲(chǔ)狀態(tài)的對(duì)象;Memento則是由Bundle類扮演。
Activity在停止之前會(huì)根據(jù)Activity的退出情景來(lái)選擇是否需要存儲(chǔ)狀態(tài),在重新啟動(dòng)該Activity時(shí)會(huì)判斷ActivityClientRecord對(duì)象中是否存儲(chǔ)了Activity的狀態(tài),如果含有狀態(tài)則調(diào)用Activity的onRestoreInstanceState函數(shù),從而使得Activity的UI效果與上次保持一致,這樣一來(lái),就保證了在非正常退出Activity時(shí)不會(huì)丟失數(shù)據(jù)的情況,很好地提升了用戶體驗(yàn)。
出處:http://huangjunbin.com/page/2/