设计模式 -- 备忘录模式
定义:
记忆一个对象的内部状态,为了允许用户取消不确定或者错误的操作,能够恢复到以前的状态。
优缺点:
优点:
1,提供可恢复机制,能够让用户恢复到历史某个状态。
2,封装细节的操作。
缺点:
貌似犯了设计模式的通病,就是类的数量增加,消耗系统资源和性能。
在android源码中的使用:
activity源码查看:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/Activity.java#Activity.onSaveInstanceState%28android.os.Bundle%29
activity状态保存:onsaveInstanceState()和onRestoreInstanceState().
onSaveInstanceState()里面:
1,存储窗口的视图树状态。
2,存储fragment的状态。
3,调用activity的activityLifecycleCallbacks的onSaveInstance函数进行状态存储。
如下是2.3的activity里面onSaveInstanceState里面的方法,保存存储窗口的视图状态。
protected void More ...onSaveInstanceState(Bundle outState) { 1087 outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState()); 1088 }
saveHierarchyState在window类里面是个抽象的方法,因此去实现类PhoneWindow去查看
强调下这里这个方法的作用:存储UI,actionBar等相关view状态。
:
代码可查看:http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/5.0.0_r2-robolectric-1/com/android/internal/policy/impl/PhoneWindow.java#PhoneWindow.saveHierarchyState%28%29
@Override public Bundle More ...saveHierarchyState() { Bundle outState = new Bundle(); if (mContentParent == null) { return outState; } SparseArray<Parcelable> states = new SparseArray<Parcelable>(); //存储整根树的结构
mContentParent.saveHierarchyState(states); //视图树结构放到outState中
outState.putSparseParcelableArray(VIEWS_TAG, states); // save the focused view id 保存获取焦点deview 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."); } } } // save the panels 存储整个面板状态 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; }
mContentParent.saveHierarchyState(states):
public void More ...saveHierarchyState(SparseArray<Parcelable> container) { dispatchSaveInstanceState(container); }
会去调用 dispatchSaveInstanceState(container)方法:看这个方法里面实现:
如无id,不保存状态,否则将状态存储在container里面。
protected void More ...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); } } }
存储信息的bundle存储在哪里?
onsaveInstanceState是在activity销毁之前,onstop之前进行的。
ActivityThread的performStopActivity里面通过调用callCallActivityonSaveInstanceState()来执行onsaveInstance
final void More ...performStopActivity(IBinder token, boolean saveState) { ActivityClientRecord r = mActivities.get(token); performStopActivityInner(r, null, false, saveState); }
performStopActivityInner(r, null, false, saveState):
private void More ...performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown, boolean saveState) { if (localLOGV) Slog.v(TAG, "Performing stop of " + r); if (r != null) { if (!keepShown && r.stopped) { if (r.activity.mFinished) { // If we are finishing, we won't call onResume() in certain // cases. So here we likewise don't want to call onStop() // if the activity isn't resumed. return; } RuntimeException e = new RuntimeException( "Performing stop of activity that is not resumed: " + r.intent.getComponent().toShortString()); Slog.e(TAG, e.getMessage(), e); } if (info != null) { try { // First create a thumbnail for the activity... // For now, don't create the thumbnail here; we are // doing that by doing a screen snapshot. info.description = r.activity.onCreateDescription(); } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to save state of activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } } // Next have the activity save its current state and managed dialogs... if (!r.activity.mFinished && saveState) { if (r.state == null) { //activity finish 且保存状态,调用activity的onsaveInstanceState方法。
callCallActivityOnSaveInstanceState(r); } } if (!keepShown) { try { //activity不可见,调用stop方法
// Now we are idle. r.activity.performStop(); } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to stop activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } r.stopped = true; } r.paused = true; } }
callCallActivityOnSaveInstanceState:
private void More ...callCallActivityOnSaveInstanceState(ActivityClientRecord r) { r.state = new Bundle(); r.state.setAllowFds(false); if (r.isPersistable()) { r.persistentState = new PersistableBundle(); mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state); } }
上面是保存状态,那么恢复状态呢?
当activity重新启动的时候,会去查询activityClientRecord,如果存在状态信息,就会调用onRestoreInstanceState()
代码可点击查看:http://grepcode.com/file_/repo1.maven.org/maven2/org.robolectric/android-all/5.0.0_r2-robolectric-1/android/app/ActivityThread.java/?v=source
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } ComponentName component = r.intent.getComponent(); if (component == null) { component = r.intent.resolveActivity( mInitialApplication.getPackageManager()); r.intent.setComponent(component); } if (r.activityInfo.targetActivity != null) { component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity); } Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + r.packageInfo.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() + ", dir=" + r.packageInfo.getAppDir()); if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.voiceInteractor); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); } activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.activity = activity; r.stopped = true; if (!r.activity.mFinished) { activity.performStart(); r.stopped = false; } //主要是这块的代码,判断state是否为null,不为null,保存了状态,调用onRestoreInstanceState函数恢复状态。
if (!r.activity.mFinished) { if (r.isPersistable()) { if (r.state != null || r.persistentState != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); } } else if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); } } if (!r.activity.mFinished) { activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnPostCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPostCreate()"); } } } r.paused = true; mActivities.put(r.token, r); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } return activity; }
Activity负责存储,恢复UI状态,也就是说负责存储备忘录,不对备忘录信息进行任何操作,只是传递给其他对象;
activity,View,ViewGroup存储对象的状态,创建备忘录,可以记录,恢复备忘录状态;
Bundle存储activity,View,ViewGroup的UI状态,存储备忘录状态。
2015年12月27日23:08:44更新
写个java的例子出来,主要涉及到状态的保存和恢复,使用备忘录模式实现:
首先原始类,要备份的对象类:
package mementopattern; public class People { //姓名 private String name; //性别 private String sex; public People() { } public People(String name, String sex) { this.name = name; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Memento createMemento() { return new Memento(name,sex); } public void setMemento(Memento memento) { this.name = memento.getName(); this.sex = memento.getSex(); } public void showPeople(){ System.out.println(toString()); } @Override public String toString() { return "People [name=" + name + ", sex=" + sex + "]"; } }
备忘录类,比起上面的类,少了创建备份和设置备份数据的方法:
public class Memento { // 姓名 private String name; // 性别 private String sex; public Memento(String name, String sex) { super(); this.name = name; this.sex = sex; } /** * @return 返回 name **/ public String getName() { return name; } /** * @param name 要设置的 name */ public void setName(String name) { this.name = name; } /** * @return 返回 sex **/ public String getSex() { return sex; } /** * @param sex 要设置的 sex */ public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Memento [name=" + name + ", sex=" + sex + "]"; } }
Caretaker类:负责存储备忘录,不对内容进行操作和访问,将备忘录传递给其他对象。
public class Caretaker { private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
测试类Test.java:
package mementopattern; public class Test { public static void main(String[] args) { People per = new People("soyoungboy","男"); Caretaker caretaker = new Caretaker(); caretaker.setMemento(per.createMemento()); per.showPeople(); per.setName("caijj"); per.setSex("女"); per.showPeople(); per.setMemento(caretaker.getMemento()); per.showPeople(); } }
运算结果:
People [name=soyoungboy, sex=男] People [name=caijj, sex=女] People [name=soyoungboy, sex=男]
其他博客看到的一篇文章,讲的有意思,可以去看看:http://www.blogjava.net/amigoxie/archive/2007/04/12/110031.html