Android View状态保存

说到状态保存,就不得不提到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<Parcelable> states = new SparseArray<Parcelable>();
    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<Parcelable> panelStates = new SparseArray<Parcelable>();
    savePanelState(panelStates);
    if (panelStates.size() > 0) {
        outState.putSparseParcelableArray(PANELS_TAG, panelStates);
    }

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

    return outState;
}

重点代码如下:

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

我们主要来看看mContentParent.saveHierarchyState(states);mContentParent是整个Activity中View视图的顶层视图。它是一个ViewGroup类型。ViewGroup里面没有实现saveHierarchyState方法,它继承自View。

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

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

@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> 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<Parcelable> 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<Parcelable> 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<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
    if (panelStates != null) {
        restorePanelState(panelStates);
    }

    if (mActionBar != null) {
        SparseArray<Parcelable> 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<Parcelable> savedStates
        = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {
    mContentParent.restoreHierarchyState(savedStates);
}

ViewGroup没有重写restoreHierarchyState方法,它继承子View,下面来看看View的restoreHierarchyState代码,

public void restoreHierarchyState(SparseArray<Parcelable> container) {
    dispatchRestoreInstanceState(container);
}

下面看看ViewGroup的dispatchRestoreInstanceState方法。

@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> 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<Parcelable> 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<N; i++) {
        Fragment f = mActive.get(i);
        if (f != null) {
            if (f.mIndex < 0) {
                throwException(new IllegalStateException(
                        "Failure saving state: active " + f
                        + " has cleared index: " + f.mIndex));
            }

            haveFragments = true;

            FragmentState fs = new FragmentState(f);
            active[i] = fs;

            if (f.mState > 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<N; i++) {
                added[i] = mAdded.get(i).mIndex;
            }
        }
    }

    // Now save back stack.
    if (mBackStack != null) {
        N = mBackStack.size();
        if (N > 0) {
            backStack = new BackStackState[N];
            for (int i=0; i<N; i++) {
                backStack[i] = new BackStackState(this, mBackStack.get(i));
                if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
                        + ": " + mBackStack.get(i));
            }
        }
    }

    FragmentManagerState fms = new FragmentManagerState();
    fms.mActive = active;
    fms.mAdded = added;
    fms.mBackStack = backStack;
    return fms;
}

下面我们来看看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<Parcelable>();
    } 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状态保存

时间: 2024-10-23 12:25:24

Android View状态保存的相关文章

Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复

Android中的状态保存和恢复 Android中的状态保存和恢复, 包括Activity和Fragment以及其中View的状态处理. Activity的状态除了其中的View和Fragment的状态之外, 还需要用户手动保存一些成员变量. Fragment的状态有它自己的实例状态和其中的View状态, 因为其生命周期的灵活性和实际需要的不同, 情况会多一些. 根据源码, 列出了Fragment中实例状态和View状态保存和恢复的几个入口, 便于分析查看. 最后专门讲了WebView状态保存和

android中正确保存view的状态

英文原文: http://trickyandroid.com/saving-android-view-state-correctly/ 转载此译文须注明出处. 今天我们聊一聊安卓中保存和恢复view状态的问题.我刻意强调View状态是因为我发现这个过程要比保存 Activity 和 Fragment状态稍微复杂,还有一个原因是因为网上有太多“重复造的轮子”(有时还是奇丑无比的轮子). 为什么我们需要保存View的状态? 这个问题问的好!我坚信移动应用应该帮助你解决问题,而不是制造问题. 想象一下

Android -- TabHost、Fragment、状态保存、通信

工程结构                                                                                       TabAFm到TabEFm都是Fragment,并且每个Fragment对应一个布局文件. TabAFm.java                                                                             package com.yydcdut.tabho

Android重写FragmentTabHost来实现状态保存

分类: android 2014-06-27 17:57 2077人阅读 评论(0) 收藏 举报 FragmentTabHost 最近要做一个类似QQ底部有气泡的功能,试了几个方案不太好,我想很多开发者使用TabHost都会知道它不保存状态,每次都要重新加载布局,为了保存状态,使用RadioGroup来实现,状态是可以保存了,问题是无法实现气泡功能,不能自定义布局,因为RadioGroup里面只能包含RadioButton,不然状态切换不起用作,这个可以查看RadioGroup源码,为了既能保存

android activity状态的保存

今天接到一个电面,途中面试官问到一个问题,如果一个activity在后台的时候,因为内存不足可能被杀死,在这之前如果想保存其中的状态数据,比如说客户填的一些信息之类的,该在哪个方法中进行. onSaveInstanceState虽然不是生命周期的方法,但只要activity在非主动的情况下杀死,就肯定会被调用. onSaveInstanceState方法被调用的需要注意的几个地方: 1.onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期

Android基础部分再学习---activity的状态保存

主要是bundle   这个參数 參考地址:http://blog.csdn.net/lonelyroamer/article/details/18715975 学习Activity的生命周期,我们知道,当Activity进入到paused或者stopped状态后,这个Activity的状态仍然保存着. 由于这个Activity对象仍然保存在内存中.它的全部信息和状态仍然是存在的.当这个Activity再次返回到前台是,它仍然保持着离开时候的样子. 然而.假设Activity进入到了后台.系统为

Android -- ViewPager、Fragment、状态保存、通信

工程架构                                                                                      TabAFm到TabEFm都是Fragment,并且每个Fragment对应一个布局文件. TabAFm.java                                                                            package com.yydcdut.viewpag

Android View转换成图片保存

package zhangphil.viewtoimage; import java.io.File; import java.io.FileOutputStream; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.view.View; import android.widget.Button; import android.widget.Tex

Activity,Fragment状态保存

Activity android:alwaysRetainTaskState     用法 用来标记Activity所在的Task的状态是否总是由系统来保持--"true",表示总是:"false",表示在某种情形下允许系统恢复Task 到它的初始化状态.默认值是"false".这个特性只针对Task的根Activity有意义:对其它Activity来说,忽略之.     一般来说,特定的情形如当用户从主画面重新选择这个Task时,系统会对这个T