zoukankan      html  css  js  c++  java
  • 设计模式 -- 备忘录模式

    定义:

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

    优缺点:

    优点:

    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

  • 相关阅读:
    搭建自己的SIPserver:开源sipserveropensips的搭建及终端TwInkle的使用
    字符串类习题、面试题具体解释(第二篇)
    【Android笔记】MediaPlayer基本使用方式
    poj 1258 Agri-Net
    ActionScript3游戏中的图像编程(连载二十四)
    SQL中declare申明变量
    CreateFileMapping使用方法
    【剑指offer】调整数组顺序
    2015美团网笔试面试总结(嵌入式/硬件类)(美团网校园招聘)
    linux串口驱动分析
  • 原文地址:https://www.cnblogs.com/androidsuperman/p/5079318.html
Copyright © 2011-2022 走看看