Android Fragment 从源码的角度去解析(下)

来源:互联网 发布:设计师用的软件 编辑:程序博客网 时间:2024/05/22 11:45

1.概述


点击打开链接  

上一篇博客已经简单的讲了一下Fragment的使用并写了一个基本的实例,接下来就将其整合到项目中。附视频地址:http://pan.baidu.com/s/1mhUus56
  
  


这里写图片描述

2.效果实现 


2.1 整合上一个实例:

  列表和轮播条不做过多的解释就是访问接口获取数据而已,这个在Android Studio自定义模板和Android无限广告轮播都讲过了。我们直接整合进去这个时候我们发现一个奇怪的问题,就是切换之后会去重新加载数据很不正常。
  一般的思路我们会换实现方法,当然其他方式肯定也可以实现如ViewPager+Fragment但是我们需要预加载要不然也会出问题,一旦预加载就需要去访问网络,即使用户可能不切换Fragment就退出App了这个时候其实加载了所有Fragment的数据,而且主页一旦复杂有可能会崩溃或造成内存溢出的问题。
  我的签名就是,忘记不了铭记,坚持不了放弃,但只要活着... 既然这样我必须得看看源码:
  
2.2 Fragment源码分析:

  把一个Fragment加到ViewGroup中就这么几行代码:add(@IdRes int containerViewId, Fragment fragment)commit(),就这么两个方法:

@Override    protected void initData() {        FragmentManager fragmentManager = getSupportFragmentManager();        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();        mHomeFragment = new HomeFragment();        fragmentTransaction.add(R.id.main_tab_fl, mHomeFragment);        fragmentTransaction.commit();    }

  我们点击add方法发现是个抽象方法:

    /**     * Calls {@link #add(int, Fragment, String)} with a null tag.     */    public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);

  点击fragmentManager.beginTransaction()发现也是一个抽象方法:

    /**     * Start a series of edit operations on the Fragments associated with     * this FragmentManager.     *      * <p>Note: A fragment transaction can only be created/committed prior     * to an activity saving its state.  If you try to commit a transaction     * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()}     * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart}     * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error.     * This is because the framework takes care of saving your current fragments     * in the state, and if changes are made after the state is saved then they     * will be lost.</p>     */    public abstract FragmentTransaction beginTransaction();

  所以只能点击getSupportFragmentManager()方法这个方法在FragmentActivity中:

    /**     * Return the FragmentManager for interacting with fragments associated     * with this activity.     */    public FragmentManager getSupportFragmentManager() {        return mFragments.getSupportFragmentManager();    }

  一路摸索才找到这个add方法,发现并没有写注释这个google工程师有点打酱油节奏啊!只好自己来吧在需要的地方写一写。我们看下面贴出来的源码其实可以知道,add方法其实只是设置了一些必要参数,并没有做任何的处理,这也是说google为什么一定要我们不要忘记commit()的原因:

    public FragmentTransaction add(int containerViewId, Fragment fragment) {        doAddOp(containerViewId, fragment, null, OP_ADD);        return this;    }    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {        fragment.mFragmentManager = mManager;        // tag可以说是唯一标识我们可以通过它从FragmentManager中找到对应的Fragment        if (tag != null) {            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {                throw new IllegalStateException("Can't change tag of fragment "                        + fragment + ": was " + fragment.mTag                        + " now " + tag);            }            fragment.mTag = tag;        }        if (containerViewId != 0) {            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {                throw new IllegalStateException("Can't change container ID of fragment "                        + fragment + ": was " + fragment.mFragmentId                        + " now " + containerViewId);            }            // 把Fragment的ContainerId和FragmentId指定为我们传递过来的布局中的ViewGroup的id。            fragment.mContainerId = fragment.mFragmentId = containerViewId;        }        // 见名思意 Op是什么?就当是一些基本参数吧        Op op = new Op();        op.cmd = opcmd;        op.fragment = fragment;        addOp(op);    }    void addOp(Op op) {        if (mHead == null) {            mHead = mTail = op;        } else {            op.prev = mTail;            mTail.next = op;            mTail = op;        }        op.enterAnim = mEnterAnim;        op.exitAnim = mExitAnim;        op.popEnterAnim = mPopEnterAnim;        op.popExitAnim = mPopExitAnim;        mNumOp++;    }

  既然add方法只是设置了一些参数而已,那么肯定就在commit()中做了些什么,找啊找啊找啊找,找到这么个方法(有些代码我就省略): 

void moveToState(Fragment f, int newState, int transit,     int transitionStyle, boolean keepActive){                // ... 省略部分代码                f.onAttach(mHost.getContext());                // 这个方法一调用就会执行Fragment的onAttach(Activity activity)这个生命周期方法                if (f.mParentFragment == null) {                    mHost.onAttachFragment(f);                }                if (!f.mRetaining) {                    f.performCreate(f.mSavedFragmentState);                    // 执行生命周期onCreate(savedInstanceState);                }                f.mRetaining = false;                if (f.mFromLayout) {                    ViewGroup container = null;                    if (f.mContainerId != 0) {                         //从activity中找到我们需要存放Fragment的ViewGroup布局                         container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);                         if (container == null && !f.mRestored) {                              throwException(new IllegalArgumentException(                                   "No view found for id 0x"                                   + Integer.toHexString(f.mContainerId) + " ("                                   + f.getResources().getResourceName(f.mContainerId)                                   + ") for fragment " + f));                        }                    }                    // For fragments that are part of the content view                    // layout, we need to instantiate the view immediately                    // and the inflater will take care of adding it.                    f.mView = f.performCreateView(f.getLayoutInflater(                        f.mSavedFragmentState), null, f.mSavedFragmentState);                    // 这个方法过后会执行onCreateView()生命周期且f.mView就是我们自己覆盖Fragment返回的View                    if (f.mView != null) {                        f.mInnerView = f.mView;                        // v4包兼容11以下的版本我还是没说错啊                        if (Build.VERSION.SDK_INT >= 11) {                            ViewCompat.setSaveFromParentEnabled(f.mView, false);                        } else {                            f.mView = NoSaveStateFrameLayout.wrap(f.mView);                        }                        if (container != null) {                            Animation anim = loadAnimation(f, transit, true,                            transitionStyle);                            if (anim != null) {                                  setHWLayerAnimListenerIfAlpha(f.mView, anim);                                  f.mView.startAnimation(anim);                            }                            // 如果ViewGroup不等于null就把从onCreateView()生命周期中获得的View添加到该布局中                            // 最主要的就是这个方法,其实我们可以把Fragment理解成一个自定义的类                            // 通过onCreateView()获取的到View添加到一个FragmentActivity的一个ViewGroup中                            // 只不过它有自己的生命周期而已......                            container.addView(f.mView);                        }                        // 如果是隐藏那就设置为不可见                        if (f.mHidden) f.mView.setVisibility(View.GONE);                        // 执行onViewCreated()生命周期方法                        f.onViewCreated(f.mView, f.mSavedFragmentState);                    } else {                        f.mInnerView = null;                    }                    f.performActivityCreated(f.mSavedFragmentState);                    if (f.mView != null) {                        f.restoreViewState(f.mSavedFragmentState);                    }                    f.mSavedFragmentState = null;                }                // 代码省略......         }}// 后面的我们就不看了,这上面的代码我自己做了一些整合,把它连贯起来了// 因为我们把add方法写在了Activity中的onCreate()方法中所以做了一些处理......

  到这里应该能够了解Fragment的工作流程了吧,接下来我们看replace方法中究竟做了?其实和add差不多只是把int opcmd变成了OP_REPLACE替换操作:

public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {        if (containerViewId == 0) {            throw new IllegalArgumentException("Must use non-zero containerViewId");        }        doAddOp(containerViewId, fragment, tag, OP_REPLACE);        return this;    }

  这个时候去commit会调用mManager.removeFragment(old, transition, transitionStyle)方法把原来的移除,然后把当前的Fragment添加进去,那岂不是每点击一个上一就被销毁了,那之前华东到哪里来了做了写什么事都被干掉重新创建了。

if (mManager.mAdded != null) {    for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {        Fragment old = mManager.mAdded.get(i);        if (old.mContainerId == containerId) {             if (old == f) {                 op.fragment = f = null;             } else {                if (op.removed == null) {                    op.removed = new ArrayList<Fragment>();                }                op.removed.add(old);                old.mNextAnim = exitAnim;                if (mAddToBackStack) {                    old.mBackStackNesting += 1;                }                mManager.removeFragment(old, transition, transitionStyle);            }        }    }}if (f != null) {    f.mNextAnim = enterAnim;    mManager.addFragment(f, false);}

这里写图片描述


  
  到这里源码就以完毕有兴趣的小伙伴可以自己仔细去看看源码,接下来我们就来解决问题,我们肯定在调用replace方法的时候希望它不要移除原来的,那怎么办改Android的底层源码吗?那就只能换方法了,思路就是如果该Fragment不存在FragmentManager中我们就去添加,否则我们把之前的隐藏而不是替换移除,把当前的显示即可,最后代码就是:

    private void homeRbClick() {        FragmentManager fragmentManager = getSupportFragmentManager();        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();        List<Fragment> fragments = fragmentManager.getFragments();        for (Fragment fragment : fragments) {            fragmentTransaction.hide(fragment);        }        fragmentTransaction.show(mHomeFragment);        fragmentTransaction.commit();    }    @OnClick(R.id.find_rb)    private void findRbClick() {        FragmentManager fragmentManager = getSupportFragmentManager();        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();        List<Fragment> fragments = fragmentManager.getFragments();        for (Fragment fragment : fragments) {            fragmentTransaction.hide(fragment);        }        if(mFindFragment == null){            mFindFragment = new FindFragment();            fragmentTransaction.add(R.id.main_tab_fl,mFindFragment);        }else {            fragmentTransaction.show(mFindFragment);        }        fragmentTransaction.commit();    }    @OnClick(R.id.new_rb)    private void newRbClick() {        FragmentManager fragmentManager = getSupportFragmentManager();        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();        List<Fragment> fragments = fragmentManager.getFragments();        for (Fragment fragment : fragments) {            fragmentTransaction.hide(fragment);        }        if(mNewFragment == null){            mNewFragment = new NewFragment();            fragmentTransaction.add(R.id.main_tab_fl,mNewFragment);        }else {            fragmentTransaction.show(mNewFragment);        }        fragmentTransaction.commit();    }    @OnClick(R.id.message_rb)    private void messageRbClick() {        FragmentManager fragmentManager = getSupportFragmentManager();        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();        List<Fragment> fragments = fragmentManager.getFragments();        for (Fragment fragment : fragments) {            fragmentTransaction.hide(fragment);        }        if(mMessageFragment == null){            mMessageFragment = new MessageFragment();            fragmentTransaction.add(R.id.main_tab_fl,mMessageFragment);        }else {            fragmentTransaction.show(mMessageFragment);        }        fragmentTransaction.commit();    }

这里已经写得太多了视频里还做了一个代码优化,具体附视频地址:http://pan.baidu.com/s/1mhUus56


这里写图片描述
0 0
原创粉丝点击