您的位置:首页 > 移动开发 > Android开发

源码解析FragmentActivity重启后数据存储问题

2017-06-06 22:10 731 查看
本文主要分析两个问题

1, 自己写Fragment里面的普通成员变量会被“清理”,但是之前设置的arguments还在,调用getArguments()还能拿到数据。

2, FragmentAcitvity由于内存重启后,里面的Fragment是否是重新创建?

 

第一个问题:getArguments()为什么能拿到数据。

内存重启,会先调用FragmentAcitvity的onSaveInstanceState,保存fragment状态在mFragments.saveAllState()这行。

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
}

 

进入saveAllState()方法,下面代码最后两行,新建了一个FragmentState,用来保存Fragment数据

Parcelable saveAllState() {
    // First collect all active fragments.
    int N = mActive.size();
    FragmentState[] active = new FragmentState
;
    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;

 

再看FragmentState的构造函数,最后一行

public FragmentState(Fragment frag) {
    mClassName = frag.getClass().getName();
    mIndex = frag.mIndex;
    mFromLayout = frag.mFromLayout;
    mFragmentId = frag.mFragmentId;
    mContainerId = frag.mContainerId;
    mTag = frag.mTag;
    mRetainInstance = frag.mRetainInstance;
    mDetached = frag.mDetached;
    mArguments = frag.mArguments;
}

mArguments= frag.mArguments; 将Fragment的arguments进行了保存,所以回答了第一个问题。

最后所有数据通过FragmentManagerState进行保存

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

并在 onSaveInstanceState 方法里,将数据存入Bundle

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
}


第二个问题:内存重启后,里面的Fragment是否是重新创建? 

答案:重新创建。

首先进入FragmentActivity的onCreate,在Bundle里取出onSaveInstanceState中保存的数据,然后进行restoreAllState。

@Override
protected void onCreate(Bundle savedInstanceState) {
    …………
    if (savedInstanceState != null) {
        Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
        mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
    }
    mFragments.dispatchCreate();
}

 

在restoreAllState里,通过FragmentState.instantiate进行创建

for (int i=0; i<fms.mActive.length; i++) {
    FragmentState fs = fms.mActive[i];
    if (fs != null) {
        Fragment f = fs.instantiate(mActivity, mParent);
        if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
        mActive.add(f);
        // Now that the fragment is instantiated (or came from being
        // retained above), clear mInstance in case we end up re-restoring
        // from this FragmentState again.
        fs.mInstance = null;
    } else {
        mActive.add(null);
        if (mAvailIndices == null) {
            mAvailIndices = new ArrayList<Integer>();
        }
        if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
        mAvailIndices.add(i);
    }
} 

进入instantiate方法,下面代码最后一行

public Fragment instantiate(FragmentActivity activity, Fragment parent) {
    if (mInstance != null) {
        return mInstance;
    }
   
    if (mArguments != null) {
        mArguments.setClassLoader(activity.getClassLoader());
    }
   
    mInstance = Fragment.instantiate(activity, mClassName, mArguments);

进入最后的Fragment.instantiate(activity, mClassName,mArguments);方法通过反射去创建。

public static Fragment instantiate(Context context, String fname, Bundle args) {
    try {
        Class<?> clazz = sClassMap.get(fname);
        if (clazz == null) {
            // Class not found in the cache, see if it's real, and try to add it
            clazz = context.getClassLoader().loadClass(fname);
            sClassMap.put(fname, clazz);
        }
        Fragment f = (Fragment)clazz.newInstance();
        if (args != null) {
            args.setClassLoader(f.getClass().getClassLoader());
            f.mArguments = args;
        }
        return f;

 

小结:内存重启,fragment的部分数据是通过FragmentActivity的onSaveInstanceState进行保存,并在OnCreate里进行恢复。由于mUserVisibleHint并没有在FragmentState里,所以如果不做好处理,会出现布局重叠。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 源码 fragment