Fragment 的正确使用与关联activity,view状态保存

Fragment正确使用以及FrameLayout的结合使用,正确的保存Fragment的状态及状态恢复

1:Fragment几个重要的生命周期方法(关联Fragment状态只介绍这几个重要的生命周期)

    private View root;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (root != null) {
            return root;
        } else {
            inflater.inflate(...)
        }
    }

相信很多人都喜欢在Fragment中保存onCreateView所需的View实例,其实这种做法是错误的,没有真正去了解Fragment的生命周期及状态保存,FragmentManager既然有保存Fragment的相关状态的方法,Fragment的View的生命周期又被FragmentTransaction所控制,在Fragment中只保存View实例引用有何意义,如果Fragment的状态没有使用得当还会容易导致Fragment的重叠

onCreate当一个fragment被创建的时候,如果FragmentTransaction.remove后再次添加会再次调用此方法,而hidden再show或者detach再attach的时候将不会再次实例化
onCreateView
onViewCreated
onActivityCreated初次创建的时候都会被执行,hide->show的时候这些方法将不会再次调用,而detach再attach的时候会再次被调用
onDestroyViewfragment的View被销毁
onDestroy fragment被移除时销毁

2:FragmentTransaction几个常用方法
add把一个fragment添加到一个Fragment管理容器里。执行上面的创建方法

remove移除Fragment管理窗口中Fragment并销毁 onDestroyView,onDestroy

replace 跟add类似,同时移除Fragment管理容器中id相同的Fragment

show 展示add到容器中的fragment的View(不会影响Fragment的
onCreate,onCreateView,onVIewCreate,onDestroy)

hide 隐藏add到容器中的fragment的View(不会影响Fragment的onCreate,onCreateView,onVIewCreate,onDestroy)

detach将fragment的View从activity中分离会执行onDestroyView()

attach将fragment的view附着到activity中会执行onCreateView,onViewCreated,onActivityCreated

了解了以上方法的使用后才可以根据需求正确的管理Fragment

3:FragmentPagerAdapter与FragmentStatePagerAdapter区别
直接看google工程师的源码来区分

FragmentStatePagerAdapter的源码

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Fragment fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment);

        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, fragment.isAdded()
                ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
        mFragments.set(position, null);

        mCurTransaction.remove(fragment);
    }


    @Override
    public Parcelable saveState() {
        Bundle state = null;
        if (mSavedState.size() > 0) {
            state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
        }
        for (int i=0; i<mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle)state;
            bundle.setClassLoader(loader);
            Parcelable[] fss = bundle.getParcelableArray("states");
            mSavedState.clear();
            mFragments.clear();
            if (fss != null) {
                for (int i=0; i<fss.length; i++) {
                    mSavedState.add((Fragment.SavedState)fss[i]);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key: keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        while (mFragments.size() <= index) {
                            mFragments.add(null);
                        }
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
        }
    }

FragmentPagerAdapter的源码


    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        mCurTransaction.detach((Fragment)object);
    }

    @Override
    public Parcelable saveState() {
        return null;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
    }

FragmentStatePagerAdater采用add,remove方式切换Fragment,当Fragment对用户不可见的时候,整个Fragment会被销毁,并在destroyItem时保存Fragment的SavedState在instantiateItem时恢复状态,并且FragmentStatePagerAdater重写saveState(),restoreState()方法,而此方法关联着ViewPager的onSaveInstanceState,onRestoreInstanceState方法,既在内存不足或横竖切屏意外回收时保存着Fragment的状态,而saveState会在Fragment创建的时候以Bundle回传给Fragment创建时的生命方法中, 因此适合用于很多界面之间的转换,而且消耗更少的内存资源。

FragmentPagerAdapter采用attach与detach方式切换Fragment,当Fragment对用户不可见时,fragment会被保存到FragmentManager管理的容器中,切换的时候还会执有fragment的引用,但是不处理内存不足或横竖切屏意外回收时的状态,因此适用于那些相对静态的页,数量也比较少的那种场景.

但是有很多APP在主模块中,ViewPager所包含的模块很多列表数据是静态的,用FragmentPagerAdapter比较合适,但在横竖切屏或内存不足重新回到界面中还要回到意外销毁前的状态该如何使用呢?这时候就需要将两者的功能结合在一块,后面再贴出源码

4:FrameLayout与Fragment的结合使用保存Fragment状态及状态恢复
FrameLayout结合Fragment的使用很简单,但是大多数同学在使用时并没有保存Fragment的状态,也就是内存不足或横竖切屏意外被回收时,(如果有用show,hidden,或attach,detach方式管理Fragment)再重新创建会出现Fragment重叠的原因, 还有在Fragment中如果有RecyclerView列表数据,在意外回收重新创建后又该如何回到销毁前滑动的位置呢?这也是为什么要先介绍google提供的FragmentPagerAdapter与FragmentStatePagerAdapter的原因

Activity有onSaveInstanceState与onRestoreInstanceState来保存与恢复意外回收时的状态,而activity又关联着activity中所有View的onSaveInstanceState与onRestoreInstanceState方法, 从ViewGroup到里面的所有View, fragment在使用时也就被关联到其中喽,大家会觉得重写Fragment的onViewStateRestored与onSaveInstanceState不就可以吗,这个只是fragment的view所对应的生命周期方法,此处介绍的是整个Fragment在附着的ViewGroup意外回收时在FragmentManager中状态的保存。大家都习惯用FrameLayout管理Fragment那就需要在FrameLayout的onSaveInstanceState与onRestoreInstanceState方法中来保存Fragment的状态,直接上代码,此处采用adapter模式封装FrameLayout管理Fragment

import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.os.ParcelableCompat;
import android.support.v4.os.ParcelableCompatCreatorCallbacks;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;

/**
 * Created by you on 2015/8/27.
 *
 * adapter模式管理fragment
 *
 */

public class FragmentFrameLayout extends FrameLayout { /** * fragment adapter */ private Adapter mAdapter; /** * 当前选中的fragment所在位置 */ private int mCurrentPosition = -1; /** * (Fragment)是否附着窗体上 */ private boolean isAttachedToWindow; /** * 内存不足时的意外回收保存的数据 */ private Parcelable mRestoredAdapterState; private int mRestoredPosition = -1; private ClassLoader mRestoredClassLoader; public FragmentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public FragmentFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public FragmentFrameLayout(Context context) { super(context); init(); } private void init() { if (getId() == View.NO_ID) { this.setId(hashCode()); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (!isAttachedToWindow) { isAttachedToWindow = true; setCurrentItem(mCurrentPosition); } } /** * 设置显示当前位置的Fragment */ public void setCurrentItem(int position) { if (mAdapter != null && position >= 0) { mCurrentPosition = position; if (isAttachedToWindow) { mAdapter.instantFragment(this, position); } } } /** * 设置管理Fragment适配器 */ public void setFrameAdapter(Adapter adapter) { this.mAdapter = adapter; if (this.mAdapter != null) { if (this.mRestoredPosition >= 0) { mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader); setCurrentItem(mRestoredPosition); mRestoredPosition = -1; mRestoredAdapterState = null; mRestoredClassLoader = null; } } } public Adapter getFrameAdapter() { return this.mAdapter; } @Override protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.position = mCurrentPosition; if (mAdapter != null) { ss.adapterState = mAdapter.saveState(); } return ss; } @Override protected void onRestoreInstanceState(Parcelable state) { if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); if (mAdapter != null) { mAdapter.restoreState(ss.adapterState, ss.loader); setCurrentItem(ss.position); } else { mRestoredPosition = ss.position; mRestoredAdapterState = ss.adapterState; mRestoredClassLoader = ss.loader; } } /** * Frame默认适配器,切换时只保存fragment对象引用,不保存状态(不会调用onCreate,onActivityCreated会调用onCreateView), * 内存不足时的意外回收会保存(Fragment.SaveState)状态 * 多层嵌套时注意FragmentManager的获取 Fragment.getChildFragmentManager() * * @author you */ public static abstract class Adapter { /** * fragmentManager */ protected final FragmentManager mManager; /** * 当前加载的Fragment */ protected Fragment mCurrentFragment; /** * 当前加载fargment的位置 */ protected int mCurrentPosition = -1; public Adapter(FragmentManager mFragmentManager) { this.mManager = mFragmentManager; } /** * fragments count * @return */ public abstract int getCount(); /** * 获取fragment * @param position * @return */ public abstract Fragment getItem(int position); public void instantFragment(FrameLayout fm, int position) { if (this.mCurrentPosition == position) return; FragmentTransaction ft = mManager.beginTransaction(); if (mCurrentFragment != null) { ft.detach(mCurrentFragment); mCurrentFragment.setMenuVisibility(false); mCurrentFragment.setUserVisibleHint(false); } final long itemId = getItemId(position); String name = makeFragmentName(fm.getId(), itemId); Fragment fragment = mManager.findFragmentByTag(name); if (fragment != null) { ft.attach(fragment); } else { fragment = getItem(position); ft.add(fm.getId(), fragment, makeFragmentName(fm.getId(), itemId)); } if (fragment != null) { fragment.setMenuVisibility(true); fragment.setUserVisibleHint(true); } mCurrentFragment = fragment; this.mCurrentPosition = position; ft.commitAllowingStateLoss(); ft = null; mManager.executePendingTransactions(); } /** * 保存fragment的状态 * @return */ public Parcelable saveState() { return null; } /** * 恢复fragment的状态, * @param state * @param loader */ public void restoreState(Parcelable state, ClassLoader loader) { } public Fragment getCurFragment() { return mCurrentFragment; } /** * fragment唯一标记 * @param position * @return */ public long getItemId(int position) { return position; } /** * 生成FragmentManager管理的tag * @param viewId * @param id * @return */ protected String makeFragmentName(int viewId, long id) { return "FrameAdapter:" + viewId + ":" + id; } } /** * 内存不足时的状态保存 */ static class SavedState extends BaseSavedState { int position; Parcelable adapterState; ClassLoader loader; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in, ClassLoader loader) { super(in); if (loader == null) { loader = getClass().getClassLoader(); } position = in.readInt(); adapterState = in.readParcelable(loader); this.loader = loader; } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(position); out.writeParcelable(adapterState, flags); } public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() { @Override public SavedState createFromParcel(Parcel in, ClassLoader loader) { return new SavedState(in, loader); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }); } }

其实原理很简单,就是根据google的FragmentPagerAdapter的原理来写的, 而Adapter也可以再拓展,


/**
 * Created by you on 15/8/31.
 * 即保存Fragment对象引用 ,切换时不调用onCreateView,onActivityCreated又保存了fragment的state(Fragment.SaveState)
 * 内存不足时的意外回收也保存了(Fragment.SaveState)
 */
public abstract class FrameAdapter extends FragmentFrameLayout.Adapter { /** * Created by you on 15/8/31. * 与adapter类似,切换时回收fragment,生命方法(onCreateView,onActivityCreated)重新调用,但保存了fragmentstate(Fragment.SaveState) * 内存不足时也会保存(Fragment.SaveState) */ public abstract class FrameStateAdapter extends FragmentFrameLayout.Adapter { 还有上节中提到的FragmentPagerAdapterFragmentStatePagerAdapter的结合 /** * Created by you on 2015/9/6. * 与SDK自带FragmentPagerAdapter不同 * ViewPager 切换时不调用onCreateView,onActivityCreated又保存了fragmentstate(Fragment.SaveState) * 内存不足时的意外回收会保存(Fragment.SaveState)状态 */ public abstract class FragmentPagerAdapter extends PagerAdapter { 

现在Studio有模拟内存不足的情况,如果懒得调直接用横竖切屏也可以,看横竖切屏后所显示的Fragment所在的position就知道状态是否有保存好

第一次写博客,中午休息时间写的,太困太困要上班了,技术有限,表达能力有限,有错误的地方,请多指点,谢谢

源码地址download.csdn.net/detail/u012…

    原文作者:Android
    原文地址: https://juejin.im/entry/58db96851b69e6006bce409a
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞