尚學堂【官網】-西安Java培訓|c++培訓|Android培訓|安卓培訓|java視頻教程|軟件工程師|-西安雁塔尚學堂計算機學校
Android安卓教程  尚學堂首頁Java學院Android安卓教程

Android源碼分析之備忘錄模式

www.zyopwk.live 發布人:java  |  來自:本站  |  發布時間:2017-09-27 11:34:06  |  點擊次數:1465

前言

剛看到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源碼分析之備忘錄模式
上一頁:Android 系統 overlay 機制重點小結
下一頁:高效開發 Android App 的 10 個建議
在線報名(*為必填項)
云南快乐十分开奖走势图