Android开发经验谈Android开发Android

Fragmentation onSupportInviable

2019-06-13  本文已影响7人  苗校长

先声明一下,这个问题有可能是我这边调用的问题,因为之前好像是正常的,不知道为什么出现了本文提到的问题,请各位大神指点一下.本人修改的源码属于头痛治头系列,没有经过严格测试会不会引发其他问题,所以慎重考虑我的方案

在项目开发中,之前一直指导fragment生命周期可见性超级复杂,项目紧,任务重,没有太多的时间来解决生命周期可见性的逻辑问题,这也是我一直不使用fagment的原因,新的项目因为需要每个页面都显示设备的状态信息,本来想用BaseActivity监听event 事件完成设备的状态监测, 然后在子activity里填充页面内容.但是总感觉不是很爽,早就关注了fragmentation这个开源库,看起来能把fragment的使用超级简化,就想着尝试用一下单activity + fragment 方案.首先不得不感谢一下作者,这个库真的是极大的简化了fragment 的使用

但是在使用过程中,也不知道是我带哦用的问题还是什么原因,出现了onSupportInvisbile 方法不调用的问题,先说一下我的项目跳转顺序

image.png

如图四个fragment顺序跳转,调用的方法是start(Fragment),我的起往是 跳转到下一个fragment 前,当前fragment 知道自己被隐层了,也就是会调用 onSupportInVisble方法,但是结果却是这样

WaitFragment}=====>onSupportVisible
WaitFragment}=====>onSupportInVisible   //注意一下只有第一个调用了 onSupportInvisible
InputCardNoFragment}=====>onSupportVisible 
BeforeTakePhotoFragment}=====>onSupportVisible
TakePhotoFragment}=====>onSupportVisible

没办法,看源码把,之前我们先注意一下,第一个也就是WaitFragment有掉onSupportInVisible方法,其他都没调

一系列的调用就不多罗嗦了,我们直接跳转到关键方法,为TransactionDelegate类的doDispatchStartTransaction(FragmentManager fm, ISupportFragment from, ISupportFragment to, int requestCode, int launchMode, int type)方法,from是调用start方法的fragmnet,to是要跳转的fragment,我把这个方法的代码跟start方法相关的代码摘了一下主要就两行

private void doDispatchStartTransaction(FragmentManager fm, ISupportFragment from, ISupportFragment to, int requestCode, int launchMode, int type) {
        checkNotNull(to, "toFragment == null");
        //获取最初的fragment
        from = getTopFragmentForStart(from, fm);
/** 一段代码表示从 当前栈顶的fragment 里拿到 containerId把toFragment填进去**/
        //开始处理start 的逻辑
        start(fm, from, to, toFragmentTag, dontAddToBackStack, sharedElementList, false, type);
    }

然后再看start的逻辑

private void start(FragmentManager fm, final ISupportFragment from, ISupportFragment to, String toFragmentTag,
                       boolean dontAddToBackStack, ArrayList<TransactionRecord.SharedElement> sharedElementList, boolean allowRootFragmentAnim, int type) {
       
        if (addMode) {
                ft.add(from.getSupportDelegate().mContainerId, toF, toFragmentTag);
                if (type != TYPE_ADD_WITHOUT_HIDE && type != TYPE_ADD_RESULT_WITHOUT_HIDE) {
//这里我们本来想hide的应该是调用start 方法的fragment,也就是上一个方法的from,但是在上一个方法中被替换成了第一个fragment,hide fragment 会调用被hide的fragment 的onSupportInVisible方法(该fragment 得是 SupportFragment的子类)
                    ft.hide(fromF);
                }
         
        supportCommit(fm, ft);
    }

大致意思就是在第二个方法中,会hide 掉from Fragment,但是在第一个方法中,传给第二个方法的from 被替换成了第一个fragment(WaitFragment),每次hide 的也都是都是第一个frgment,这样也跟我们的日志是符合的,即只有第一frgment 会调用onSupoortInVisible方法.

所以我们现在可以推测,之所以当前fragment 跳转到下一个fragment之前不能调onSupportInviaible,原因是当前fragment没有按照预想的顺序位于栈顶

这个时候我们在查看栈视图,果然发现这些fragment在栈内的顺序是乱七八糟的,很难找到规律..

那接下来就是要排查为什么当前fragment不能位于栈顶呢,我们继续查看获取栈顶frgment 的方法:getTopFragmentForStart

  private ISupportFragment getTopFragmentForStart(ISupportFragment from, FragmentManager fm) {
        ISupportFragment top;
        if (from == null) {
            top = SupportHelper.getTopFragment(fm);
        } else {
            if (from.getSupportDelegate().mContainerId == 0) {
                Fragment fromF = (Fragment) from;
                if (fromF.getTag() != null && !fromF.getTag().startsWith("android:switcher:")) {
                    throw new IllegalStateException("Can't find container, please call loadRootFragment() first!");
                }
            }
            top = SupportHelper.getTopFragment(fm, from.getSupportDelegate().mContainerId);
        }
        return top;
    }

核心代码就是
SupportHelper.getTopFragment(fm, from.getSupportDelegate().mContainerId)

再进去看这个方法:

 public static ISupportFragment getTopFragment(FragmentManager fragmentManager, int containerId) {
        List<Fragment> fragmentList = FragmentationMagician.getActiveFragments(fragmentManager);
        if (fragmentList == null) return null;

        for (int i = fragmentList.size() - 1; i >= 0; i--) {
            Fragment fragment = fragmentList.get(i);
            if (fragment instanceof ISupportFragment) {
                ISupportFragment iFragment = (ISupportFragment) fragment;
                if (containerId == 0) return iFragment;

                if (containerId == iFragment.getSupportDelegate().mContainerId) {
                    return iFragment;
                }
            }
        }
        return null;
    }

意思大致就是从FragmentationMagician.getActiveFragments(fragmentManager)中获取到最末尾的装在containerId中的 SupportFragment,本例四个fragment 全中.

再看getActiveFragments 的源码:

 if (!(fragmentManager instanceof FragmentManagerImpl))
            return Collections.EMPTY_LIST;
        // For pre-25.4.0
        if (sSupportLessThan25dot4) return fragmentManager.getFragments();

        // For compat 25.4.0+
        try {
            FragmentManagerImpl fragmentManagerImpl = (FragmentManagerImpl) fragmentManager;
            // Since v4-25.4.0,mActive: ArrayList -> SparseArray
            return getActiveList(fragmentManagerImpl.mActive);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fragmentManager.getFragments();

support 版本0超过25.4.0,会走return getActiveList(fragmentManagerImpl.mActive);这个方法,

private static List<Fragment> getActiveList(HashMap<String, Fragment> active) {
        if (active == null) {
            return Collections.EMPTY_LIST;
        }
        final int count = active.size();
        ArrayList<Fragment> fragments = new ArrayList<>(count);
        fragments.addAll(active.values());
        return fragments;
    }

意思就是将参数的值封装到list中,之前调用时传入的参数是fragmentManagerImpl.mActive 点进去一看,阿西吧!!!原来是个HashMap,这顺序怎么保证!!!自然也就乱套了..但是令人感到奇怪的是,点返回键出栈的顺序居然和我们设想的一样,也没再研究作者是怎么做到的.

final ArrayList<Fragment> mAdded = new ArrayList<>();
final HashMap<String, Fragment> mActive = new HashMap<>();

至于这个mActive 和mAdd的fragment 的区别,我看了半天也没搞明白,但是至少madded 是有顺序的,暂时先返回这个吧...有什么隐患以后再说把..于是修改FragmentMagiciangetActiveFragments方法,强行返回那个有顺序的list:

 public static List<Fragment> getActiveFragments(FragmentManager fragmentManager) {
        if (!(fragmentManager instanceof FragmentManagerImpl))
            return Collections.EMPTY_LIST;
      
        return fragmentManager.getFragments();
    }

调整后的日志,符合预期,看栈的顺序也符合预期了..出栈正常,有什么其他问题,暂时还没有测试,目前满足这种简单的但是需要可见性检测的场景

上一篇下一篇

猜你喜欢

热点阅读