編輯:關於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狀態保存
最近做項目真是頭疼呢?之前想用ListViewAnnotation來著,就是可以實現類似於android 通知欄滑動刪除的效果。好像是一位大牛自己一個人寫的
Android-ListView兩種適配器ListView在安卓App中非常常見,幾乎每一個App都會有涉及,比如QQ消息列表,或者是通訊錄還有就是酷我音樂的歌曲列表都是
橙子VR app是一款提供3D/全景視頻播放、VR游戲下載、VR熱點資訊的虛擬現實內容聚合平台。橙子VR app是一個優質VR視頻3D電影電視劇動
之前的一篇概要文章中主要說了我這次研究的一些具體情況,這裡就不在多說了,但是這裡還需要指出的是,感謝一下三位大神願意分享的知識(在我看來,懂得分享和細致的人才算是大神,不