DialogFragment dismiss闪退异常&DialogFragment何时持有FragmentManager

使用DialogFragment如果在没有调用过show方法时调用dismiss方法,就会出现异常
java.lang.IllegalStateException: Fragment DialogFragmentName(你自己的类名) not associated with a fragment manager.

提示就是没有FragmentManager

public class DialogFragment extends Fragment{

    public void dismiss() {
        dismissInternal(false, false);
        //可以看到直接调用了dismissInternal方法,这也是DialogFragment中的方法
    }
    
    void dismissInternal(boolean allowStateLoss, boolean fromOnDismiss) {		
    	//...
		//上面有一部分代码,和我们遇到的异常无关
		//下面进入了一个if语句,不管是进入if还是else
		//都要调用getParentFragmentManager方法
        if (mBackStackId >= 0) {
            getParentFragmentManager().popBackStack(mBackStackId,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
                    //...
        } else {
            FragmentTransaction ft = getParentFragmentManager().beginTransaction();
            //...
        }
        //...
    }

getParentFragmentManager()是Fragment的方法,会返回Fragment的mFragmentManager对象,当mFragmentManager为null时,就会抛出我们遇到的异常

    @NonNull
    public final FragmentManager getParentFragmentManager() {
        FragmentManager fragmentManager = mFragmentManager;
        if (fragmentManager == null) {
            throw new IllegalStateException(
                    "Fragment " + this + " not associated with a fragment manager.");
        }
        return fragmentManager;
    }

DialogFragment.dismiss->DialogFragment.dismissInternal->Fragment.getParentFragmentManager,最终因为Fragment的mFragmentManager变量是null导致抛出异常。

怎么才能让这个变量不是null呢,答案是在调用DialogFragment.show方法时需要传入一个FragmentManager对象作为参数,最终这个对象会被赋值给当前DialogFragment的mFragmentManager变量,之后再调用dismiss方法也不会抛出异常了

解决方法:
避免在没有DialogFramgent没有show过的情况下调用dismiss
或是重写dismiss方法,手动判断在mFragmentManager不为null时才dismiss

    @Override
    public void dismiss() {
        if (getFragmentManager()==null){
            Log.w(TAG, "dismiss: "+this+" not associated with a fragment manager." );
        }else {
            super.dismiss();
        }
    }

番外:show方法是怎么给Fragment的mFragmentManager变量赋值的?

顺便分享一下自己追代码的方式。

先试着正着跟了一下,FragmentManager和BackStackRecord来回跳,基本追了几层就放弃了,选择先倒着看一下,也就是看下Fragment的mFragmentManager在什么地方赋值,来找点线索或者目标,我们不用弄清楚每个赋值的方法具体做了什么,只要往上追两层,对相关方法的名字或者相应的动作有印象就好了。
在这里插入图片描述
可以看到,Fragment的mFragmentManager的有效赋值只在BackStackRecordFragmentManagerFragmentLayoutInflaterFactory三个类中

然后我们重新从show方法开始追下去,如果追到这三个类之外的方法,并且方法参数不是这三个类的话(如果是这三个类,可能还会转回来),基本就可以放弃了,可以少追很多无效代码

DialogFragment.show(FragmentManager manager)

        FragmentTransaction ft = manager.beginTransaction();//直接返回一个BackStackRecord对象,它是抽象类FragmentTransaction的唯一实现类
        ft.add(this, tag);//把当前的DialogFragment(是Fragment的子类)封装到一个对象里,并添加到ft对象持有的集合中
        ft.commit();//跟进去

BackStateRecord.commit()&commitInteral()
commit是FragmentTranslation中定义的抽象方法,BackStateRecord对它的实现是直接调用自己的commitInteral方法

    int commitInternal(boolean allowStateLoss) {
		//...
        mManager.enqueueAction(this, allowStateLoss);//跟进去,有经验的话,看到这个方法名字就觉得很可能是它。可能接收的参数就是一个类似Runnable的实现类,在调用 类似run()的方法时,还是要去这个实现类中看逻辑的
       	//but!很可惜,这里给Fragemnt设置FragemtnManager和这个this参数无关哈哈
        //...
    }

FragmentManager.enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss)

    void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) {
        synchronized (mPendingActions) {
            mPendingActions.add(action);//与Fragment设置FragmentManager无关,大概看了下,是用来打印log的
            scheduleCommit();//跟进去
        }
    }

FragmentManager.scheduleCommit()

    void scheduleCommit() {
    	//...
        mHost.getHandler().removeCallbacks(mExecCommit);//看名字就不用管
        mHost.getHandler().post(mExecCommit);//post,这个参数是一个Runnable,看下它的run方法
        updateOnBackPressedCallbackEnabled();//点进去看一眼,是设置了某个回调是否启用,跟这个方法的话需要继续去看这个回调的逻辑,优先级比较低,没办法了再去看它
        /...
    }
    private Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions(true);
        }
    };

FragmentManager.execPendingActions

    boolean execPendingActions(boolean allowStateLoss) {
        ensureExecReady(allowStateLoss);//由于之前倒着追的时候,对这个方法名或类似的名字有印象,所以我选择直接跟这个方法,如果无效的话再看后面的
        //...
    }

FragmentManager.ensureExecReady()

    private void ensureExecReady(boolean allowStateLoss) {
		//...
        executePostponedTransaction(null, null);
        //...
    }

FragmentManager.executePostponedTransaction

    private void executePostponedTransaction(@Nullable ArrayList<BackStackRecord> records,
            @Nullable ArrayList<Boolean> isRecordPop) {
            //...
                if (records != null && !listener.mIsBack
                        && (index = records.indexOf(listener.mRecord)) != -1
                        && isRecordPop != null
                        && isRecordPop.get(index)) {
                    // This is popping a postponed transaction
                    listener.cancelTransaction();//这是倒着追的时候看到过的方法
                } else {
                    listener.completeTransaction();//这也是倒着追的时候看到过的方法
                }
            }
        }
    }

看到这里,基本可以确定就是这个方法里进行的赋值了
他们两个方法都调用了mRecord.mManager.completeExecute这个方法

    void completeExecute(@NonNull BackStackRecord record, boolean isPop, boolean runTransitions,
            boolean moveToState) {
        if (isPop) {
            record.executePopOps(moveToState);//倒着追看到过
        } else {
            record.executeOps();//也看到过
        }
    }

这两个BackStackRecord类中的方法点进去,都是进行了一个类似的switch语句,其中会遍历DialogFragment.show()-> ft.add()中的集合,这个集合中的对象是持有一个Fragment的;
最后一步冲刺!!
BackStackRecord.executeOps()

            switch (op.mCmd) {
                case OP_ADD:
                    f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
                    mManager.setExitAnimationOrder(f, false);
                    mManager.addFragment(f);//就是它
                    break;
                case OP_REMOVE:
                    f.setAnimations(op.mEnterAnim, op.mExitAnim, op.mPopEnterAnim, op.mPopExitAnim);
                    mManager.removeFragment(f);
                    break;
                    //...
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值