設計模式備忘錄模式

1.備忘錄模式的定義及使用場景
備忘錄模式是一種行為模式,該模式用于保存對象當前狀態,并且在之后可以再次恢復到此狀態。備忘錄模式實現的方式需要保證被保存的對象狀態不能被對象從外部訪問,目的是為了保護好被保存的這些對象狀態的完整性以及內部實現不向外暴露。
定義:
在不破壞封閉的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態,這樣,以后就可將該對象回復到原先保存的狀態
使用場景:

需要保存一個對象在某一個時刻的狀態或部分狀態
需要用一個接口來讓其他對象得到這些狀態,將會暴露對象的實現細節并破壞對象的封裝性,一個對象不希望外界直接訪問其內部狀態,通過中間對象可以間接訪問其內部狀態

Paste_Image.png

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時不會丟失數據的情況,很好地提升了用戶體驗。
出處:http://huangjunbin.com/page/2/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容