code小生,一个专注 Android 领域的技术平台
公众号回复 Android 加入我的安卓技术群
作者:DDDong丶
链接:https://www.jianshu.com/p/c8e8a0249911
声明:本文已获DDDong丶授权发表,转发等请联系原作者授权
问题分析
一直在简书里看别人的技术贴,今天我也来写点自己的心得!最近在写一个项目用到大量的Fragment后的总结!
我想刚刚接触安卓的同学或许会这么写:
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.add(ViewId,fragment);// 或者fragmentTransaction.replace(ViewId,fragment);
fragmentTransaction.commit();
基础更好一点的同学会用show和hide方法
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.hide(new FirstFragment())
        .show(new SecondFragment())
        .commit();
诚然这两种都可以切换Fragment,但是面对用户大量点击来回切换,或者你的Fragment本来就很多,每次都这样操作,那么很快你的应用就会OOM,就算不崩那也会异常的卡顿!so why?
当我们replace时发生了以下的生命周期:

想想看每次都replace一下!!这世界会有多美好!!!那么问题出在哪?回过头看看代码就会发现每次在add/replace或者show/hide都会new 一个新的实例,这就是致命原因!!!!!
废话不多说,亮出我的方法(抽取后的):
 /**
 *  Fragment的添加
 * @param manager Fragment管理器
 * @param aClass 相应的Fragment对象的getClass
 * @param containerId 容器的id
 * @param args 需要传值的话可将bundle填入  不需要传值就填null
 */
protected void addFragment(FragmentManager manager, Class extends BaseFragment> aClass, int containerId, Bundle args) {
    String tag = aClass.getName();
    Logger.d("%s add fragment %s", TAG, aClass.getSimpleName());
    Fragment fragment = manager.findFragmentByTag(tag);
    FragmentTransaction transaction = manager.beginTransaction(); // 开启一个事务
    if (fragment == null) {// 没有添加
        try {
            fragment = aClass.newInstance(); // 通过反射 new 出一个 fragment 的实例
            BaseFragment baseFragment = (BaseFragment) fragment; // 强转成我们base fragment
            // 设置 fragment 进入,退出, 弹进,弹出的动画
            transaction.setCustomAnimations(baseFragment.enter(), baseFragment.exit(), baseFragment.popEnter(), baseFragment.popExit());
            transaction.add(containerId, fragment, tag);
            if (baseFragment.isNeedToAddBackStack()) { // 判断是否需要加入回退栈
                transaction.addToBackStack(tag); // 加入回退栈时制定一个tag,以便在找到指定的事务
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    } else {
        if (fragment.isAdded()) {
            if (fragment.isHidden()) {
                transaction.show(fragment);
            }
        } else {
            transaction.add(containerId, fragment, tag);
        }
    }
    if (fragment != null) {
        fragment.setArguments(args);
        hideBeforeFragment(manager, transaction, fragment);
        transaction.commit();
    }
}
/**
 * 除当前 fragment 以外的所有 fragment 进行隐藏
 *
 * @param manager
 * @param transaction
 * @param currentFragment
 */
private void hideBeforeFragment(FragmentManager manager, FragmentTransaction transaction, Fragment currentFragment) {
    List fragments = manager.getFragments();for (Fragment fragment : fragments) {if (fragment != currentFragment && !fragment.isHidden()) {
            transaction.hide(fragment);
        }
    }
}略微解释一下:
先查询fragmentManager 所在的activitiy 中是否已经添加了这个fragment
第一步 先从一个mAdded 的一个ArrayList遍历查找,如果找不到再从 一个 叫 mActive 的 SparseArray的一个map里面查找。
注意:
- 一个 fragment 被 remove 掉后,只会从 mAdded 里面删除,不会从 mActive 里面删除,只有当这个fragment 所在的 transaction 从回退栈里面移除后才会 从mActive 删除 
- 当我们add 一个fragment时 会把我们的fragment 添加到 mAdded 里面,不会添加到 mActive。 
- 只有当我们把 transaction 添加到回退栈的时候,才会把我们的 fragment 添加到 mActive 里面。所以我们通过 findFragmentByTag 方法查找出来的 fragment 不一定是被添加到我们的 activity 中。 
使用:
代码比较多,但是我个人感觉使用起来比较方便,而且功能也比较完善,使用的时候只需要两行代码:
HomeFragment1 homeFragment = new HomeFragment1();
addFragment(getSupportFragmentManager(),homeFragment.getClass(),R.id.main_body,null);
当我们需要传值的时候,只需要将准备好的bundle以参数的形式填入即可。
我还增加了一个是否加入回退栈的判断,用于实现一些有关回退栈的需求,实现这个功能还需要在BaseFragment中定义一个方法:
protected boolean isNeedToAddBackStack() {
        return true;
}
也就这么点内容,各位大佬如果看出什么问题或者有什么更好的方法,欢迎大家在下方评论留言。
推荐阅读
浅谈 Activity,Fragment 模块化封装
巧用 Fragment,解耦 Android6.0 权限适配手记

不怕巨人高
就怕巨人还要踩在巨人肩膀上
这样就很难超越了