Android 多 Fragment 切换优化

 
 

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();
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();
ft.hide(new FirstFragment())
        .show(new SecondFragment())
        .commit();

诚然这两种都可以切换Fragment,但是面对用户大量点击来回切换,或者你的Fragment本来就很多,每次都这样操作,那么很快你的应用就会OOM,就算不崩那也会异常的卡顿!so why?

当我们replace时发生了以下的生命周期:

640?wx_fmt=other
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);        }    }}
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);
addFragment(getSupportFragmentManager(),homeFragment.getClass(),R.id.main_body,null);

当我们需要传值的时候,只需要将准备好的bundle以参数的形式填入即可。
我还增加了一个是否加入回退栈的判断,用于实现一些有关回退栈的需求,实现这个功能还需要在BaseFragment中定义一个方法:

protected boolean isNeedToAddBackStack() {        return true;}
        return true;
}

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

推荐阅读
浅谈 Activity,Fragment 模块化封装
巧用 Fragment,解耦 Android6.0 权限适配手记

640?wx_fmt=jpeg

不怕巨人高

就怕巨人还要踩在巨人肩膀上

这样就很难超越了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值