Android源码分析之备忘录模式

1,895 阅读7分钟

前言

刚看到Java设计模式中的备忘录模式,心思一转,就想到了Android开发中Activity的两个重要的方法onSaveInstanceState和onRestoreInstanceState,这两个方法能够保证我们在开发应用时,遇到未知问题,导致Activity非正常退出时候,在Activity在随后时间被系统杀死之前会回调这两个方法,存储记录Activity相关的信息,以便在下次返回Activity的时候能够恢复这些数据。

Android源码分析

之前文章讲到了Java设计模式中的备忘录模式,今天就根据这个模式来看看Android中是如何实现备忘录模式的(源码基于Android6.0)。

  • 首先来看一下Activity的onSaveInstanceState方法

      final void performSaveInstanceState(Bundle outState) {
          onSaveInstanceState(outState);
          saveManagedDialogs(outState);
          mActivityTransitionState.saveState(outState);
          if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
      }
  • 由上面可以看出,Android6.0源码将onSaveInstanceState包含在了performSaveInstanceState中,具体的onSaveInstanceState方法如下

      protected void onSaveInstanceState(Bundle outState) {
          //1.存储当前窗口的视图树状态,调用的是windoe的实现类phonewindow的方法
          outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
          //2.存储Fragments的状态
          Parcelable p = mFragments.saveAllState();
          if (p != null) {
              outState.putParcelable(FRAGMENTS_TAG, p);
          }
          //3.如果用户设置了Activity的ActivityLifecycleCallbacks
          //那么调用这些ActivityLifecycleCallbacks的onSaveInstanceState进行存储
          getApplication().dispatchActivitySaveInstanceState(this, outState);
      }

    上面的方法分为三部分:
    (1)存储窗口的视图树的状态
    (2)存储fragment的状态
    (3)调用Activity的ActivityLifecycleCallbacks的onSaveInstanceState方法进行存储状态

  • 下面我们来看第一步,Window的saveHierarchyState由其实现类PhoneWindow的saveHierarchyState方法实现,具体代码如下:

      @Override
      public Bundle saveHierarchyState() {
          Bundle outState = new Bundle();
          if (mContentParent == null) {
              return outState;
          }
      SparseArray<Parcelable> states = new SparseArray<Parcelable>();
      // 1.调用mContentParent的saveHierarchyState方法,保存当前视图内容,这里存储着整个视图树的内容
      mContentParent.saveHierarchyState(states);
      // 将视图树结构放到outState中
      outState.putSparseParcelableArray(VIEWS_TAG, states);
      // 2.保存当前界面的中获取的焦点信息
      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.");
              }
          }
      }
      // 3.保存整个面板的状态
      SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
      savePanelState(panelStates);
      if (panelStates.size() > 0) {
          outState.putSparseParcelableArray(PANELS_TAG, panelStates);
      }
      // 4.保存actionbar的状态
      if (mDecorContentParent != null) {
          SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
          mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
          outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
      }
      return outState;

    }

  • 上面方法中分别保存了页面的主要信息,包括UI,actionbar的相关信息。其中这个mContentParent是我们通过Activity的setContentView方法设置的内容视图,它是整个内容视图的根节点,存储了它的层级结构中的view状态,就相当于存储了用户界面的状态。它是一个ViewGroup对象,但这个saveHierarchyState方法是View的一个方法,如下:

      public void saveHierarchyState(SparseArray<Parcelable> container) {
          // ViewGroup调用的父类View的方法,其父类View用调用此方法存储状态
          dispatchSaveInstanceState(container);
      }
  • View方法没有直接存储,而是调用dispatchSaveInstanceState方法间接存储,这里便是真正存储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获取自身状态(View的默认状态空)
              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.以key为id,state为value存储到container中
                  container.put(mID, state);
              }
          }
      }
  • View自身的onSaveInstanceState方法

      @CallSuper
      protected Parcelable onSaveInstanceState() {
          mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
          // View类默认的存储状态为空
          if (mStartActivityRequestWho != null) {
              BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
              state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
              return state;
          }
          return BaseSavedState.EMPTY_STATE;
      }
  • 在View类中的saveHirearchyState方法中调用dispatchSaveInstanceState方法来存储自身的状态,而ViewGroup则覆写了dispatchSaveInstanceState方法来存储自身以及子视图的状态,如下:

      @Override
      protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
          //ViewGroup覆写View的dispatchSaveInstanceState方法,保存自身的状态
          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);
              }
          }
      }
  • 在ViewGroup的dispatchSaveInstanceState方法中,首先调用super.dispatchSaveInstanceState存储自身的状态,然后遍历子视图,调用子视图的dispatchSaveInstanceState方法来存储它们的状态。其中在View的具体保存过程中我们可以看出,只有View设置了唯一性的id,View才会进行记录。此外,在View中我们看到返回的是空状态,这意味着我们需要存储View状态时,需要覆写onSaveInstanceState方法,将要存储的数据放到Parcelable并将它返回。这里我们可以看一下TextView的实现过程:

      @Override
      public Parcelable onSaveInstanceState() {
          Parcelable superState = super.onSaveInstanceState();
          // Save state if we are forced to
          boolean save = mFreezesText;
          int start = 0;
          int end = 0;
          if (mText != null) {
              start = getSelectionStart();
              end = getSelectionEnd();
              if (start >= 0 || end >= 0) {
                  // Or save state if there is a selection
                  save = true;
              }
          }
          //存储TextView的start,end以及文本内容
          if (save) {
              SavedState ss = new SavedState(superState);
              // XXX Should also save the current scroll position!
              ss.selStart = start;
              ss.selEnd = end;
              if (mText instanceof Spanned) {
                  Spannable sp = new SpannableStringBuilder(mText);
                  if (mEditor != null) {
                      removeMisspelledSpans(sp);
                      sp.removeSpan(mEditor.mSuggestionRangeSpan);
                  }
                  ss.text = sp;
              } else {
                  ss.text = mText.toString();
              }
              if (isFocused() && start >= 0 && end >= 0) {
                  ss.frozenWithFocus = true;
              }
              ss.error = getError();
              if (mEditor != null) {
                  ss.editorState = mEditor.saveInstanceState();
              }
              return ss;
          }
          return superState;
      }
  • 调用View的onSaveInstanceState方法后就得到了View要存储的数据,到这里便执行到了第三步。至此,经过一层层的遍历,整个内容视图树便存储下来了。

      if (state != null) {
          // Log.i("View", "Freezing #" + Integer.toHexString(mID)
          // + ": " + state);
          // 3.以key为id,state为value存储到container中
          container.put(mID, state);
      }
  • 存储完Window的视图树信息后,便执行存储Fragment的状态信息、回退栈等。这个存储Fragment的状态信息也是调用它的onSaveInstanceState方法,存储Fragment中View视图树状态,最好就是调用用户设置的ActivityLifecycleCallbacks的onSaveInstanceState方法,让用户能够再做一些额外的处理。到这里,整个存储过程就完成了。


  • 上面分析了Activity在未知状态下销毁前存储的信息,这些存储的信息都保存在了Bundle数据中,那系统又是如何恢复数据的呢?在Activity被销毁onStop方法执行之前,系统会调用ActivityThread的performStopActivity方法,如下:

      //包含stop方法
      final void performStopActivity(IBinder token, boolean saveState) {
          // 获取ActivityClientRecord对象
          ActivityClientRecord r = mActivities.get(token);
          // 执行方法,saveState就是表示是否要存储的状态
          performStopActivityInner(r, null, false, saveState);
      }
    
      private void 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 (info != null) {
                  try {
                      info.description = r.activity.onCreateDescription();
                  } catch (Exception e) {
                      //省略
                  }
              }
              // 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的OnStop函数
                      r.activity.performStop();
                  } catch (Exception e) {
                     //省略
                  }
                  r.stopped = true;
              }
              r.paused = true;
          }
      }
    
      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 {
              // 调用mInstrumentation的callActivityOnSaveInstanceState函数
              // 实际上调用的是Activity的callActivityOnSaveInstanceState函数
              mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
          }
       }
    
      public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
          activity.performSaveInstanceState(outState);
      }
  • 在performStopActivity中,通过token获取一个ActivityClientRecord对象,这个对象就保存了Acvtivyt的信息。之后调用performStopActivityInner,其方法执行大致分为4部:
    (1)判断是否需要存储Activtiy的状态
    (2)如果需要存储,调用onSaveInstanceState方法
    (3)将信息存储到ActivityClientRecord对象的stat字段中
    (4)调用Actvity的onStop方法

  • 由上可以知道,在onStop方法执行之前,系统会根据情况选择是否存储Actvity的状态,并且将这些状态保存在mActivities中,这个mActivities维护了一个Activity的信息表,当Activity重启时候,会从mActivities中查询到对应的ActivityClientRecord,如果有信息,则调用Activity的onResoreInstanceState方法,在ActivityThread的performLaunchActivity方法中,具体如下:

       private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
          // 省略
          Activity activity = null;
          try {
              java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
              // 1.构建Activity
              activity = mInstrumentation.newActivity(
                      cl, component.getClassName(), r.intent);
              //省略
          } catch (Exception e) {
              //省略
          }
          try {
              // 2.创建一个Application
              Application app = r.packageInfo.makeApplication(false, mInstrumentation);
              if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
              if (activity != null) {
                  // 3.创建Context,类型为ContextImpl
                  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);
                  // 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);
    
                  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;
                  // 5.调用Activity的OnCreate方法
                  if (r.isPersistable()) {
                      mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                  } else {
                      mInstrumentation.callActivityOnCreate(activity, r.state);
                  }
                  if (!activity.mCalled) {
                      //省略
                  }
                  r.activity = activity;
                  r.stopped = true;
                  if (!r.activity.mFinished) {
                      activity.performStart();
                      r.stopped = false;
                  }
                  // 6.调用Actvity的OnRestoreInstanceState恢复初始状态
                  if (!r.activity.mFinished) {
                     //省略
                  }
                  if (!r.activity.mFinished) {
                     //省略
                  }
              }
              r.paused = true;
              // 将Activity的信息记录对象存到mActivities中
              mActivities.put(r.token, r);
          } catch (SuperNotCalledException e) {
              throw e;
    
          } catch (Exception e) {
             //省略
          }
    
          return activity;
      }
  • 上面可以看出,系统会判断ActivityClientRecord对象的state是否为空,不为空则通过Activity的onSaveInstanceState获取其UI状态信息,通过这些信息传递给Activity的onCreate方法,使得用户可以在onCreate方法中恢复UI上的状态。

总结
以上的分析可以看出,在整个过程中,Activity扮演了CareTaker角色,负责存储、恢复Ui的状态信息;Activity、Fragment、View等对象为Originator角色,也就是扮演存储状态的对象;Memoto则是有Bundle类扮演,单纯的负责数据的支持(容器)。Activit在异常退出时,会根据情况,选择是否需要存储相关状态信息,在重新启动时候,也会根据ActivityClientRecord对象是否存储Activity的状态,选择性的恢复其初始状态。这样下来,就保证了在非正常情况下销毁Activity时不会丢失数据,很好的提升用户体验。

深度拓展

备忘录设计模式实战解析

参考文献:Android源码设计模式解析与实战