Fragmentation onSupportInviable

作者: 苗校长 | 来源:发表于2019-06-13 18:35 被阅读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();
        }
    

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

    相关文章

      网友评论

        本文标题:Fragmentation onSupportInviable

        本文链接:https://www.haomeiwen.com/subject/lzysfctx.html