Android源码设计模式学习笔记-备忘录模式

备忘录这种设计模式用来保存一个对象的属性备份,它的uml图如下


image.png

通常我们会有需求要求保存一个对象的一些属性表现为另外一个对象的形式作为备份,如上面的uml类图中Originator这个对象可以创建一个Memoto用来保存Originator的一些属性(成员变量)这个对象可以保存到Creataker中.

一个简单的需求展示如何使用该模式.

通常我们玩游戏的时候都会去存档,在这里就可以使用备忘录模式来完成存档的过程:
show code

public class Memoto {
    public int mCheckPoint = 1;
    public int mLifeValue = 100;
    public String mWeapon = "沙漠之鹰";

    @Override
    public String toString() {
        return "Memoto{" +
                "mCheckPoint=" + mCheckPoint +
                ", mLifeValue=" + mLifeValue +
                ", mWeapon='" + mWeapon + '\'' +
                '}';
    }
}
public class CallOfDuty {
    private int mCheckPoint = 1;
    private int mLifeValue = 100;
    private String mWeapon = "沙漠之鹰";

    public void play(){
        System.out.println("玩游戏 : "+String.format("第%d关",mCheckPoint)+" 奋战杀敌中");
        mLifeValue -= 10;
        System.out.println("进度升级啦");
        mLifeValue++;
        System.out.println("到达 "+String.format("第%d关",mCheckPoint)+" 关");
    }

    //退出游戏
    public void quit(){
        System.out.println("-----------");
        System.out.println("退出前的游戏属性 : "+this.toString());
        System.out.println("退出游戏");
        System.out.println("-----------");
    }

    /**
     * 创建备忘录
     */
    public Memoto createMemoto(){
        Memoto memoto = new Memoto();
        memoto.mCheckPoint = mCheckPoint;
        memoto.mLifeValue = mLifeValue;
        memoto.mWeapon = mWeapon;
        return memoto;
    }

    public void restore(Memoto memoto){
        mCheckPoint = memoto.mCheckPoint;
        mLifeValue = memoto.mLifeValue;
        mWeapon = memoto.mWeapon;
    }

    @Override
    public String toString() {
        return "CallOfDuty{" +
                "mCheckPoint=" + mCheckPoint +
                ", mLifeValue=" + mLifeValue +
                ", mWeapon='" + mWeapon + '\'' +
                '}';
    }
}
public class Caretaker {
    private Memoto mMemoto;        //备忘录
    /**
     * 存档
     */
    public void archive(Memoto memoto){
        this.mMemoto = memoto;
    }
    /**
     * 获取存档
     */
    public Memoto getMemoto(){
        return mMemoto;
    }
}

最后调用

public static void main(String[] args){
        CallOfDuty game = new CallOfDuty();
        game.play();
        Caretaker caretaker = new Caretaker();
        caretaker.archive(game.createMemoto());
        game.quit();
        CallOfDuty newGame = new CallOfDuty();
        newGame.restore(caretaker.getMemoto());
}

这里利用Caretaker来存储CallOfDuty的状态(CallOfDuty也就是uml中的Originator).

Android中的备忘录模式
protected void onSaveInstanceState(Bundle outState) {
        //1.存储当前窗口的视图树状态
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        //2.存储Fragment状态
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        //3.如果用户还设置了Activity的ActivityLifeCycleCallBacks那么调用ActivityLifeCycleCallBacks的onSaveInstanceState进行存储状态
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }

从上面代码来看onSaveInstanceState主要做了下面三个操作
(1)存储窗口的视图树状态
(2)存储Fragment的状态
(3)调用Activity的ActivityLifeCycleCallBacks的onSaveInstanceState进行存储状
我们先来看看第一步中存储窗口的视图树状态,Activity中的Window是PhoneWindow,我们看PhoneWindow中saveHierarchyState是如何实现的.

 @Override
    public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();
        if (mContentParent == null) {
            return outState;
        }
        //通过SparseArray来存储,这相当于一个key为整形的map
        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        //调用mContentParent的saveHierarchyState方法,这个mContentParent就是调用Activity的setContentView函数设置的内容视图,它是内容视图的根节点,在这里存储整棵视图树的结构.
        mContentParent.saveHierarchyState(states);
        //将视图树放在outState中
        outState.putSparseParcelableArray(VIEWS_TAG, states);

        // 保存当前获取焦点的view.
        final View focusedView = mContentParent.findFocus();
        //持有焦点的view必须设置id,否则重新进入该界面时不会恢复其焦点状态
        if (focusedView != null && focusedView.getId() != View.NO_ID) {
            outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
        }

       //存储整个面板的状态
        SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
        savePanelState(panelStates);
        if (panelStates.size() > 0) {
            outState.putSparseParcelableArray(PANELS_TAG, panelStates);
        }
      
        if (mDecorContentParent != null) {
            SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
            mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
            outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
        }

        return outState;
}

这里着重分析一下mContentParent这一个过程,mContentParent就是在Activity中setContentView设置的内容视图,它是整个内容视图的根节点,存储它的层级结构view状态也就存储了用户界面的状态。mContentParent是一个viewgroup对象但是saveHierarchyState并不是viewgroup的对象,它是在view中的。

public void saveHierarchyState(SparseArray<Parcelable> container) {
    dispatchSaveInstanceState(container);
}

真正存储view状态的函数

  protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        //1 注意这里view中如果没有id,那么这个view状态将不会被存储.
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
        //2 调用onSaveInstanceState函数获取自身的状态
            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);
                //3 将自身状态存放在container中,key为id , value为自身的状态.
                container.put(mID, state);
            }
        }
    }

@CallSuper
    @Nullable protected Parcelable onSaveInstanceState() {
        //view本身默认返回
        return BaseSavedState.EMPTY_STATE;
    }

viewgroup还重写了dispatchSaveInstanceState函数,它的dispatchSaveInstanceState函数如下:

@Override
    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        super.dispatchSaveInstanceState(container);
        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            View c = children[i];
            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                c.dispatchSaveInstanceState(container);
            }
        }
    }

这里我们可以看到dispatchSaveInstanceState实现中首先会调用 super.dispatchSaveInstanceState去保存它自身的状态,然后会调用它子view的dispatchSaveInstanceState,这里其实有点像事件的向下传递过程,它是一个树形的结构.
上面代码中我们还需要注意到注释1的地方,它是指如果一个view没有id它是不会被保存状态的,这里的id实际上是我们在xml中定义的id.用来标示view的唯一性.
下面我们来分析一下TextView的onSaveInstanceState过程.

@Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();

        // Save state if we are forced to
        final boolean freezesText = getFreezesText();
        boolean hasSelection = false;
        int start = -1;
        int end = -1;

        if (mText != null) {
            start = getSelectionStart();
            end = getSelectionEnd();
            if (start >= 0 || end >= 0) {
                // Or save state if there is a selection
                hasSelection = true;
            }
        }

        if (freezesText || hasSelection) {
            SavedState ss = new SavedState(superState);

            if (freezesText) {
                if (mText instanceof Spanned) {
                    final Spannable sp = new SpannableStringBuilder(mText);

                    if (mEditor != null) {
                        removeMisspelledSpans(sp);
                        sp.removeSpan(mEditor.mSuggestionRangeSpan);
                    }

                    ss.text = sp;
                } else {
                    ss.text = mText.toString();
                }
            }

            if (hasSelection) {
                // XXX Should also save the current scroll position!
                ss.selStart = start;
                ss.selEnd = end;
            }

            if (isFocused() && start >= 0 && end >= 0) {
                ss.frozenWithFocus = true;
            }

            ss.error = getError();

            if (mEditor != null) {
                ss.editorState = mEditor.saveInstanceState();
            }
            return ss;
        }

        return superState;
    }

可以看到它返回了一个SavedState对象,SavedState是实现了Parcelable接口,此时正好返回到注释2这个位置. 然后被put到container中.
在保存了Activity的状态后,接下来就是Fragment的状态了, 这个Fragment状态的存储过程也是调用onSaveInstanceState方法,过程和Activity其实很类似。 最后就是调用用户设置的AcvitityLifecycleCallbacks的onSaveInstanceState方法,让用户也再做一些额外的处理,至此,整个存储过程就完成了.
经过一层一层的循环,最后的存储对象都放在了Bundle中,那么这个Bundle是存储到哪的呢?
onSaveInstanceState是在Activity onStop()之前, Activity的onStop方法在ActivityThread的performStopActivity函数中,相关代码如下:

    final void performStopActivity(IBinder token, boolean saveState, String reason) {
        //获取ActivityClientRecord
        ActivityClientRecord r = mActivities.get(token);
        //执行performStopActivityInner,saveState标示是否要存储状态
        performStopActivityInner(r, null, false, saveState, reason);
    }
private void performStopActivityInner(ActivityClientRecord r,
            StopInfo info, boolean keepShown, boolean saveState, String reason) {

            // Next have the activity save its current state and managed dialogs...
            if (!r.activity.mFinished && saveState) {
                if (r.state == null) {
                    //执行Activity的OnSaveInstanceState函数
                    callCallActivityOnSaveInstanceState(r);
                }
            }

            if (!keepShown) {
                try {
                    // Now we are idle.
                    //执行Activity的OnSaveInstanceState函数
                    r.activity.performStop(false /*preserveWindow*/);
                } 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;
                EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
                        r.activity.getComponentName().getClassName(), reason);
            }
        }
    }
 private void 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 {
            //调用Instrumentation的callActivityOnSaveInstanceState函数,实际上会调用Activity的onSaveInstanceState
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }

performStopActivity中通过token获得一个ActivityClientRecord对象,这个对象就是用来存储保存view状态的Bundle对象. performStopActivityInner这个函数主要做了如下操作:
(1),判断是否要存储Activity状态
(2),如果需要存储Activity状态,调用onSaveInstanceState函数
(3),将存储的信息放在ActivityClientRecord的state字段中
(4),Activity的onStop方法
这里我们将view状态保存到ActivityClientRecord中,ActivityClientRecord是放在mActivities中的,mActivities维护了一个Activity信息表,当Activity重启后会查询mActivities中对应的ActivityClientRecord信息,那么则会调用Activity的onRestoreInstanceState函数.

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //代码省略
        //1 创建Context,类型为ContextImpl
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            //2 构建Activity
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            //代码省略
        } catch (Exception e) {
        }

        try {
            //3 创建一个Application
            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) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                //4 关联appContext, Application对象到Activity中
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                //5 调用Activity的OnCreate方法.
                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;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            //6 调用Activity的OnRestoreInstanceState恢复状态
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
               
            }
            r.paused = true;
            //7 将Activity信息记录在mActivities中
            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
             //
        } catch (Exception e) {
            //
        }

        return activity;
    }

注释6处,系统会判断ActivityClientRecord对象中的state是否为空,如果不为空则说明了存储了该Activity状态,此时会通过OnRestoreInstanceState恢复保存在ActivityClientRecord中的ui状态信息.
在这个过程中
Activity扮演Creataker角色
Bundle扮演Memoto角色
View,ViewGroup,Fragment扮演Originator角色

总结:
备忘录模式通过存储一个对象成员变量的快照,在合适的时候恢复,从而达到一个备份的效果.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容