View源碼——saveInstanceState

基于api28

setter getter

    public boolean isSaveEnabled() {
        return (mViewFlags & SAVE_DISABLED_MASK) != SAVE_DISABLED;
    }

    public void setSaveEnabled(boolean enabled) {
        setFlags(enabled ? 0 : SAVE_DISABLED, SAVE_DISABLED_MASK);
    }

這里對應的標志位是SAVE_DISABLED

還有兩個類似的方法:

    public boolean isSaveFromParentEnabled() {
        return (mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED;
    }

    public void setSaveFromParentEnabled(boolean enabled) {
        setFlags(enabled ? 0 : PARENT_SAVE_DISABLED, PARENT_SAVE_DISABLED_MASK);
    }

這里對應的標志位是PARENT_SAVE_DISABLED

兩個標志位有什么區(qū)別呢?我們來看下整個事件的傳遞過程就知道了。這里只看saveInstanceState的傳遞,restoreInstanceState是一樣的。

事件傳遞

saveInstanceState事件最早由ActivityThread發(fā)起。
ActivityThread源碼不再貼了,清楚調用時機即可:

  1. api < 11,onSaveInstance在onPause之前執(zhí)行
  2. 11 <= api < 28,onSaveInstance在onPause之后,onStop之前執(zhí)行
  3. api >= 28,onSaveInstance在onStop之后執(zhí)行

Activity:

    //由ActivityThread調用
    final void performSaveInstanceState(Bundle outState) {
        onSaveInstanceState(outState);
        ...
    }

    protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
        ...
    }

這里的mWindow是PhoneWindow。

PhoneWindow:

@Override
    public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();
        if (mContentParent == null) {
            return outState;
        }

        //當前窗口內的view的狀態(tài)都會保存在這個SparseArray中
        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        //調用view的方法,開始遍歷視圖樹
        mContentParent.saveHierarchyState(states);
        ...
        return outState;
    }

View:

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

    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        //想要保存自身狀態(tài),需要設置id,并且未設置SAVE_DISABLED_MASK標志位
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            //調用onSaveInstanceState,真正實現保存自身狀態(tài)
            Parcelable state = onSaveInstanceState();
            if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                throw new IllegalStateException(
                        "Derived class did not call super.onSaveInstanceState()");
            }
            if (state != null) {
                // 最終放到這里保存
                container.put(mID, state);
            }
        }
    }

    @CallSuper
    @Nullable protected Parcelable onSaveInstanceState() {
        ...
    }

ViewGroup:

    @Override
    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        //先調用super.dispatchSaveInstanceState來保存自身的狀態(tài)
        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) {
                //未設置PARENT_SAVE_DISABLED才會傳遞給c,
                //否則,c及其子View都不會收到saveInstanceState事件
                c.dispatchSaveInstanceState(container);
            }
        }
    }

結論如下:

  1. setSaveEnabled(boolean enabled)僅僅影響自身,不會影響其子View
  2. setSaveFromParentEnabled(boolean enabled)同時影響自身及其子View
  3. 要真正實現保存自身狀態(tài),還需要為View設置id
  4. View的id作為SparseArray中的鍵值,還必須在視圖樹中保證唯一性,否則會造成混亂

關于第四點,特意試驗了一下,id相同的兩個view,在onSaveInstance中保存自己的hashCode,然后在onRestoreInstanceState讀取出來,結果如下:

2019-01-25 15:30:02.385 8499-8499/com.sollian.sourcestudy D/MyTextView: onSaveInstanceState: 73681134
2019-01-25 15:30:02.386 8499-8499/com.sollian.sourcestudy D/MyTextView: onSaveInstanceState: 50315663
2019-01-25 15:30:03.389 8499-8499/com.sollian.sourcestudy D/MyTextView: onRestoreInstanceState: 50315663
2019-01-25 15:30:03.389 8499-8499/com.sollian.sourcestudy D/MyTextView: onRestoreInstanceState: 50315663

可以看到,第一個view的數據被后面的覆蓋了。

最后,onRestoreInstanceState的調用時機在onStart之后,onResume之前。附上一段生命周期圖:

圖1
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 本系列出于AWeiLoveAndroid的分享,在此感謝,再結合自身經驗查漏補缺,完善答案。以成系統(tǒng)。 Andro...
    濟公大將閱讀 1,995評論 2 10
  • 第一章:Activity生命周期和啟動模式 Activity關閉時會調用onPause()和onStop(),如果...
    loneyzhou閱讀 1,066評論 0 2
  • Activity的生命周期和啟動模式 1.1 Activity的生命周期全面分析 將 Activity 的生命周期...
    Yi__Lin閱讀 687評論 0 3
  • 17:00,加班結束,太陽還未下山,便決定同著夕陽遠去的方向步行回家。 久未走這條路,傍晚時分,散步的人們依舊:或...
    今心安閱讀 437評論 0 4
  • 今天我在一營的群里面練聲,被宇彤老師翻牌點評了,非常開心。 關于嘿哈音,以前練錯了,嘿和哈 都靠前,喉嚨放松,發(fā)聲...
    小游219閱讀 1,758評論 0 1

友情鏈接更多精彩內容