您的位置:首页 > 其它

设计模式 -- 备忘录模式

2015-12-26 23:40 399 查看
定义:

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

优缺点:

优点:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: