Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android View狀態保存

Android View狀態保存

編輯:關於Android編程

說到狀態保存,就不得不提到Activity的onSaveInstanceState()方法,這個是大家經常用到的一個函數,就是當我們的Activity被置為後台,當我們再次進入這個Activity的時候,這個Activity需要被恢復,並且回調這個方法。

下面來看看這個方法

private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";

protected void onSaveInstanceState(Bundle outState) {
    // 1、對Window裡面的View樹進行狀態保存
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    // 2、對Fragmet進行狀態保存
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    getApplication().dispatchActivitySaveInstanceState(this, outState);
}

可以看到上面分為兩個部分:

1、對View樹進行狀態保存

2、對Fragment進行狀態保存

因為我們的Activity的視圖是由View層次樹或者Fragment構成。

一、View樹狀態保存

我們來看看mWindow.saveHierarchyState()方法。它對應的是PhoneWindow.saveHierarchyState()。

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

    // 1、創建一個狀態數組,所有view的狀態都存放在這個狀態數組中
    SparseArray states = new SparseArray();
    mContentParent.saveHierarchyState(states);
    outState.putSparseParcelableArray(VIEWS_TAG, states);

    // save the focused view id
    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 panelStates = new SparseArray();
    savePanelState(panelStates);
    if (panelStates.size() > 0) {
        outState.putSparseParcelableArray(PANELS_TAG, panelStates);
    }

    if (mActionBar != null) {
        SparseArray actionBarStates = new SparseArray();
        mActionBar.saveHierarchyState(actionBarStates);
        outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
    }

    return outState;
}

重點代碼如下:

SparseArray states = new SparseArray();
mContentParent.saveHierarchyState(states);
outState.putSparseParcelableArray(VIEWS_TAG, states);

我們主要來看看mContentParent.saveHierarchyState(states);mContentParent是整個Activity中View視圖的頂層視圖。它是一個ViewGroup類型。ViewGroup裡面沒有實現saveHierarchyState方法,它繼承自View。

public void saveHierarchyState(SparseArray container) {
    dispatchSaveInstanceState(container);
}

下面來看看ViewGroup中的dispatchSaveInstanceState方法。

@Override
protected void dispatchSaveInstanceState(SparseArray container) {
   // 1、調用父類的dispatchSaveInstanceState方法,就是調用View的dispatchSaveInstanceState方法
   // 目的是保存ViewGroup的當前狀態
    super.dispatchSaveInstanceState(container);

    // 2、遍歷ViewGroup的子View,調用每個子View的dispatchSaveInstanceState方法
    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);
        }
    }
}

上面就是首先保存自己的當前狀態,然後進行遞歸保存所有子View的當前狀態

下面看看View的dispatchSaveInstanceState方法

protected void dispatchSaveInstanceState(SparseArray container) {
    if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
        mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
        //1、獲取當前View需要保存的Parcelable
        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);
            // 2、將該View的state保存到SparseArray類型的container
            // container就是之前創建的states,存儲方式是以該view的id作為key
            container.put(mID, state);
        }
    }
}

從這裡我們知道,如果一個view需要保存它的當前狀態,就必須給這個view一個id,因為它將作為key來存放該view的狀態。

另外,如果我們需要保存一個View的當前狀態,我們可以重寫onSaveInstanceState方法,把需要保存的內容進行保存進可以了。

二、View樹狀態保存恢復
視圖樹保存下來的SparsesArray不是被ViewGroup自己持有,而是整個視圖樹狀態保存之後會放到一個Bundle中。所以View樹狀態恢復首先就是需要拿到這個Bundle對象,在Activity的onCreate(Bundle)和onRestoreInstanceState(Bundle)我們都可以拿到這個對象。

下面來看看Activity的onRestoreInstanceState(Bundle)方法。

protected void onRestoreInstanceState(Bundle savedInstanceState) {
    if (mWindow != null) {
        // 1、得到保存的狀態
        Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
        if (windowState != null) {
            // 2、狀態恢復
            mWindow.restoreHierarchyState(windowState);
        }
    }
}

下面來看看PhoneWindow的restoreHierarchyState函數

@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
    if (mContentParent == null) {
        return;
    }
    // 得到所有View保存的狀態
    SparseArray savedStates
            = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
    if (savedStates != null) {
        mContentParent.restoreHierarchyState(savedStates);
    }

    // restore the focused view
    int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
    if (focusedViewId != View.NO_ID) {
        View needsFocus = mContentParent.findViewById(focusedViewId);
        if (needsFocus != null) {
            needsFocus.requestFocus();
        } else {
            Log.w(TAG,
                    "Previously focused view reported id " + focusedViewId
                            + " during save, but can't be found during restore.");
        }
    }

    // restore the panels
    SparseArray panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
    if (panelStates != null) {
        restorePanelState(panelStates);
    }

    if (mActionBar != null) {
        SparseArray actionBarStates =
                savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
        if (actionBarStates != null) {
            mActionBar.restoreHierarchyState(actionBarStates);
        } else {
            Log.w(TAG, "Missing saved instance states for action bar views! " +
                    "State will not be restored.");
        }
    }
}

重點代碼:

SparseArray savedStates
        = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {
    mContentParent.restoreHierarchyState(savedStates);
}

ViewGroup沒有重寫restoreHierarchyState方法,它繼承子View,下面來看看View的restoreHierarchyState代碼,

public void restoreHierarchyState(SparseArray container) {
    dispatchRestoreInstanceState(container);
}

下面看看ViewGroup的dispatchRestoreInstanceState方法。

@Override
protected void dispatchRestoreInstanceState(SparseArray container) {
    // 1、執行父類的dispatchRestoreInstanceState方法,也就是View的dispatchRestoreInstanceState方法
    // 目的是恢復當前ViewGroup的狀態

    // 2、遞歸遍歷所有的子View來恢復所有子View的狀態
    super.dispatchRestoreInstanceState(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.dispatchRestoreInstanceState(container);
        }
    }
}

下面看看View的dispatchRestoreInstanceState方法。

protected void dispatchRestoreInstanceState(SparseArray container) {
    if (mID != NO_ID) {
        // 1、得到這個View保存的狀態
        Parcelable state = container.get(mID);
        if (state != null) {
            // Log.i("View", "Restoreing #" + Integer.toHexString(mID)
            // + ": " + state);
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            // 2、回調onRestoreInstanceState進行狀態恢復
            onRestoreInstanceState(state);
            if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                throw new IllegalStateException(
                        "Derived class did not call super.onRestoreInstanceState()");
            }
        }
    }
}

所以如果我們的一個View重寫了onSaveInstanceState()方法進行了狀態的保存,相應的就應該重寫onRestoreInstanceState方法進行相應的狀態恢復。

三、Fragment狀態保存

上面我們在Activity的onSaveInstanceState方法中執行了mFragments.saveAllState()進行了Fragment的狀態保存,mFragments是一個FragmentManagerImpl類型的對象,我們來看看它的saveAllState方法。

Parcelable saveAllState() {

    // First collect all active fragments.
    int N = mActive.size();
    FragmentState[] active = new FragmentState[N];
    boolean haveFragments = false;
    for (int i=0; i Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                // 重點方法就是執行saveFragmentBasicState來保存Fragment的狀態
                fs.mSavedFragmentState = saveFragmentBasicState(f);

                if (f.mTarget != null) {
                    if (fs.mSavedFragmentState == null) {
                        fs.mSavedFragmentState = new Bundle();
                    }
                    putFragment(fs.mSavedFragmentState,
                            FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
                    if (f.mTargetRequestCode != 0) {
                        fs.mSavedFragmentState.putInt(
                                FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
                                f.mTargetRequestCode);
                    }
                }

            } else {
                fs.mSavedFragmentState = f.mSavedFragmentState;
            }
        }
    }

    int[] added = null;
    BackStackState[] backStack = null;

    // Build list of currently added fragments.
    if (mAdded != null) {
        N = mAdded.size();
        if (N > 0) {
            added = new int[N];
            for (int i=0; i 0) {
            backStack = new BackStackState[N];
            for (int i=0; i

下面我們來看看saveFragmentBasicState方法

Bundle saveFragmentBasicState(Fragment f) {
    Bundle result = null;

    if (mStateBundle == null) {
        mStateBundle = new Bundle();
    }
    f.performSaveInstanceState(mStateBundle);
    if (!mStateBundle.isEmpty()) {
        result = mStateBundle;
        mStateBundle = null;
    }
    // 因為Fragment裡面View視圖也是View樹
    // 下面就是保存Fragment中的View樹的狀態
    if (f.mView != null) {
        saveFragmentViewState(f);
    }
    if (f.mSavedViewState != null) {
        if (result == null) {
            result = new Bundle();
        }
        result.putSparseParcelableArray(
                FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
    }
    if (!f.mUserVisibleHint) {
        if (result == null) {
            result = new Bundle();
        }
        // Only add this if it's not the default value
        result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
    }

    return result;
}

下面來看看saveFragmentViewState方法

void saveFragmentViewState(Fragment f) {
    if (f.mView == null) {
        return;
    }
    if (mStateArray == null) {
        mStateArray = new SparseArray();
    } else {
        mStateArray.clear();
    }

    // 這裡應該比較熟悉了,跟上面一樣就是對這個View樹的狀態進行遞歸遍歷保存
    f.mView.saveHierarchyState(mStateArray);
    if (mStateArray.size() > 0) {
        f.mSavedViewState = mStateArray;
        mStateArray = null;
    }
}

四、Fragment狀態恢復

我們來看看Activity的onCreate(Bundle)方法

protected void onCreate(@Nullable Bundle savedInstanceState) {
    if (savedInstanceState != null) {
        // 得到保存的狀態
        Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
        // 狀態恢復
        mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.fragments : null);
    }
}

可以看到它調用的是mFragments.restoreAllState方法,mFragments是一個FragmentManagerImpl類型的對象,我們來看看它的restoreAllState方法。

參考文章:Android 視圖樹&View狀態保存

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved