设计模式 -- 备忘录模式

定义:

记忆一个对象的内部状态,为了允许用户取消不确定或者错误的操作,能够恢复到以前的状态。

优缺点:

优点:

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

posted @ 2015-12-26 23:40  西北野狼  阅读(300)  评论(0编辑  收藏  举报