Android 多 Fragment 切换优化

来自: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时发生了以下的生命周期:

image

想想看每次都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<Fragment> fragments = manager.getFragments();

    for (Fragment fragment : fragments) {
        if (fragment != currentFragment && !fragment.isHidden()) {
            transaction.hide(fragment);
        }
    }
}

略微解释一下:

先查询fragmentManager 所在的activitiy 中是否已经添加了这个fragment
第一步 先从一个mAdded 的一个ArrayList遍历查找,如果找不到再从 一个 叫 mActive 的 SparseArray的一个map里面查找。

注意:

  1. 一个 fragment 被 remove 掉后,只会从 mAdded 里面删除,不会从 mActive 里面删除,只有当这个fragment 所在的 transaction 从回退栈里面移除后才会 从mActive 删除

  2. 当我们add 一个fragment时 会把我们的fragment 添加到 mAdded 里面,不会添加到 mActive。

  3. 只有当我们把 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;
}

也就这么点内容,各位大佬如果看出什么问题或者有什么更好的方法,欢迎大家在下方评论留言。

推荐↓↓↓
安卓开发
上一篇:Flutter 体验记