'FragmentHostCallback.getHandler()' on a null object reference

本文深入探讨了在使用Fragment进行权限请求时遇到的空指针异常问题,详细分析了错误堆栈,通过源码跟踪定位到FragmentManager的ensureExecReady()方法中mHost.getHandler().getLooper()出现空指针的具体原因。文章给出了明确的解决方案,在调用commitNowAllowingStateLoss()前检查FragmentManager状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Attempt to invoke virtual method ‘android.os.Handler android.support.v4.app.FragmentHostCallback.getHandler()’ on a null object reference

最近写的一个动态权限库中用到了Fragment进行权限的请求和管理,但是在上线后发现bugly爆了一个null指针的问题闲来没事就去跟了跟源码

想对Fragment有更深入了解的点这里哦:Fragment 和 Activity 关联源码梳理

出错的本地代码
            rxPermissionsFragment = RxPermissionsFragment()
            fragmentManager
                    .beginTransaction()
                    .add(rxPermissionsFragment, TAG)
                    .commitNowAllowingStateLoss() //这一行
bugly上显示的出错堆栈

android.support.v4.app.FragmentManagerImpl.ensureExecReady(TbsSdkJava:1959)

可以看出来是FragmentManager中的ensureExecReady()方法。定位到该方法:

    /**
     * Broken out from exec*, this prepares for gathering and executing operations.
     *
     * @param allowStateLoss true if state loss should be ignored or false if it should be
     *                       checked.
     */
    private void ensureExecReady(boolean allowStateLoss) {
        if (mExecutingActions) {
            throw new IllegalStateException("FragmentManager is already executing transactions");
        }
				
				//注意这一行
        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
            throw new IllegalStateException("Must be called from main thread of fragment host");
        }

        if (!allowStateLoss) {
            checkStateLoss();
        }

        if (mTmpRecords == null) {
            mTmpRecords = new ArrayList<>();
            mTmpIsPop = new ArrayList<>();
        }
        mExecutingActions = true;
        try {
            executePostponedTransaction(null, null);
        } finally {
            mExecutingActions = false;
        }
    }

发现是由于mHost.getHandler().getLooper()出现了空指针,接下来去定位到mHost的初始化和销毁的方法:

    public void dispatchDestroy() {
        mDestroyed = true;
        execPendingActions();
        mExecutingActions = true;
        moveToState(Fragment.INITIALIZING, false);
        mExecutingActions = false;
        mHost = null;
        mContainer = null;
        mParent = null;
    }

发现实际上就是在Fragment的onDestroy中会调用,再往上看发现会在Activity的onDestroy中进行调用。

结论

根据上面的源码追踪,最终造成这个异常的原因就是当调用FragmentManager的commitNowAllowingStateLoss()时该Fragment的关联Activity已经调用过onDestroy,也就是已经被销毁。

解决方案

在调用commitNowAllowingStateLoss()之前判断FragmentManager的状态,如:

       //避免异常情况下Activity被销毁后继续调用导致异常
        if (fragmentManager.isDestroyed) {
            return null
        }
private Watchdog() { 511 mThread = new Thread(this::run, "watchdog"); 512 513 // Initialize handler checkers for each common thread we want to check. Note 514 // that we are not currently checking the background thread, since it can 515 // potentially hold longer running operations with no guarantees about the timeliness 516 // of operations there. 517 // 518 // Use a custom thread to check monitors to avoid lock contention from impacted other 519 // threads. 520 ServiceThread t = new ServiceThread("watchdog.monitor", 521 android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); 522 t.start(); 523 mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread", mLock); 524 mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker)); 525 526 mHandlerCheckers.add( 527 withDefaultTimeout( 528 new HandlerChecker(FgThread.getHandler(), "foreground thread", mLock))); 529 // Add checker for main thread. We only do a quick check since there 530 // can be UI running on the thread. 531 mHandlerCheckers.add( 532 withDefaultTimeout( 533 new HandlerChecker( 534 new Handler(Looper.getMainLooper()), "main thread", mLock))); 535 // Add checker for shared UI thread. 536 mHandlerCheckers.add( 537 withDefaultTimeout(new HandlerChecker(UiThread.getHandler(), "ui thread", mLock))); 538 // And also check IO thread. 539 mHandlerCheckers.add( 540 withDefaultTimeout(new HandlerChecker(IoThread.getHandler(), "i/o thread", mLock))); 541 // And the display thread. 542 mHandlerCheckers.add( 543 withDefaultTimeout( 544 new HandlerChecker(DisplayThread.getHandler(), "display thread", mLock))); 545 // And the animation thread. 546 mHandlerCheckers.add( 547 withDefaultTimeout( 548 new HandlerChecker( 549 AnimationThread.getHandler(), "animation thread", mLock))); 550 // And the surface animation thread. 551 mHandlerCheckers.add( 552 withDefaultTimeout( 553 new HandlerChecker( 554 SurfaceAnimationThread.getHandler(), 555 "surface animation thread", 556 mLock))); 557 // Initialize monitor for Binder threads. 558 addMonitor(new BinderThreadMonitor()); 559 560 mInterestingJavaPids.add(Process.myPid()); 561 这部分代码中哪些是monitorchecker添加,哪部分是handlerchecker添加
最新发布
07-26
<think>我们正在分析Watchdog的构造函数代码片段。需要区分哪些是添加MonitorChecker,哪些是添加LooperChecker(即HandlerChecker针对不同线程的实例)。 根据之前的讨论: - MonitorChecker 实际上是HandlerChecker的一种,用于检查实现了Watchdog.Monitor接口的服务。 - 在代码中,HandlerChecker有两种使用方式: 1. 用于监控Monitor对象(通过HandlerChecker的post功能,实际在monitor线程中执行monitor()方法) 2. 用于监控线程的Looper(检查消息处理是否超时) 具体到代码片段: 1. 第520-524行:创建了一个专门用于监控的线程(ServiceThread),并基于这个线程的Looper创建了一个HandlerChecker,这个Checker就是用于Monitor的,即mMonitorChecker。 它被添加到mHandlerCheckers列表中(mHandlerCheckers是一个包含所有Checker的列表)。 2. 第526行开始,添加了一系列针对不同系统线程的HandlerChecker(如foreground thread, main thread, ui thread等)。这些Checker都是用来监控对应线程的Looper的消息处理时间的,属于LooperChecker。 3. 第558行:通过addMonitor方法添加了一个BinderThreadMonitor。addMonitor方法内部会将这个Monitor对象添加到mMonitorChecker中(即之前创建的专门用于Monitor的HandlerChecker)。 因此,我们可以总结: - MonitorChecker的添加:在构造函数中,首先创建了一个专门的HandlerChecker(mMonitorChecker)并添加到mHandlerCheckers中。然后通过addMonitor方法添加具体的Monitor实现(如BinderThreadMonitor)到mMonitorChecker中。 - LooperChecker的添加:从第526行到第556行,通过创建多个HandlerChecker(每个绑定到不同线程的Handler)并添加到mHandlerCheckers中,这些就是用于监控各个线程Looper的Checker。 注意:在代码中,所有的Checker都是HandlerChecker类的实例,但根据它们绑定的对象不同(Monitor对象 vs Looper对象)而功能不同。 具体代码对应: - MonitorChecker(即用于Monitor的HandlerChecker)的添加: 520: ServiceThread t = new ServiceThread(...); 522: t.start(); 523: mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread", mLock); 524: mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker)); - 然后,通过addMonitor方法(第558行)将具体的Monitor实现(BinderThreadMonitor)添加到mMonitorChecker中。 - LooperChecker(即用于监控Looper的HandlerChecker)的添加: 从第526行开始,连续添加了多个HandlerChecker,每个都绑定到一个特定线程的Handler(即该线程的Looper): foreground thread (FgThread) main thread (主线程Looper) ui thread (UiThread) i/o thread (IoThread) display thread (DisplayThread) animation thread (AnimationThread) surface animation thread (SurfaceAnimationThread) 因此,在构造函数中,添加了一个MonitorChecker(用于所有Monitor)和多个LooperChecker(用于各个关键线程)。 注意:在Watchdog中,实际上只有两种HandlerChecker:一种是绑定到Monitor线程(用于执行monitor()方法)的,另一种是绑定到其他线程的Looper(用于检查消息处理时间)的。但它们的基类都是HandlerChecker。 所以,回答: 1. MonitorChecker的添加:通过创建mMonitorChecker(一个HandlerChecker实例,它使用一个专门的monitor线程的Looper)并添加到mHandlerCheckers中,然后通过addMonitor方法将具体的Monitor对象(如BinderThreadMonitor)添加到这个mMonitorChecker中。 2. LooperChecker的添加:通过创建多个HandlerChecker实例,每个实例绑定到一个系统线程的Handler(即Looper),并添加到mHandlerCheckers中。 注意:在代码中,并没有一个单独的类叫MonitorChecker或LooperChecker,它们都是HandlerChecker类,只是初始化时传入的参数不同(一个是专门用于执行monitor的线程的Handler,另一个是各个系统线程的Handler)以及使用方式不同(一个用于执行monitor()方法,一个用于检查消息队列)。 因此,在构造函数中: - 第523行创建的是MonitorChecker(功能上,但类型是HandlerChecker) - 第524行将其添加到总列表 - 第526-556行创建的是多个LooperChecker(同样,功能上,类型也是HandlerChecker)并添加到总列表 最后,第558行通过addMonitor方法将BinderThreadMonitor添加到mMonitorChecker中,这样mMonitorChecker就会在检查时调用BinderThreadMonitor的monitor()方法。 总结添加部分: MonitorChecker(即用于Monitor的HandlerChecker)的初始化:第520-524行(创建并添加到列表),然后第558行添加具体的Monitor对象。 LooperChecker(用于监控线程的HandlerChecker)的初始化:第526-556行(创建多个并添加到列表)。</think>在`Watchdog`构造函数代码中,`MonitorChecker`和`LooperChecker`(即线程监控的HandlerChecker)的添加逻辑如下: ### 一、MonitorChecker 添加部分 1. **专用监控线程创建**(第520-522行) ```java ServiceThread t = new ServiceThread("watchdog.monitor", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); t.start(); ``` 创建名为"watchdog.monitor"的专用服务线程,避免与其他线程锁竞争 2. **MonitorChecker初始化**(第523行) ```java mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread", mLock); ``` 基于监控线程的Looper创建HandlerChecker实例 3. **添加到检查列表**(第524行) ```java mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker)); ``` 将MonitorChecker加入全局检查队列 ### 二、LooperChecker 添加部分(线程监控) 1. **前台线程监控**(第526-528行) ```java new HandlerChecker(FgThread.getHandler(), "foreground thread", mLock) ``` 2. **主线程监控**(第531-534行) ```java new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread", mLock) ``` 3. **UI线程监控**(第536-537行) ```java new HandlerChecker(UiThread.getHandler(), "ui thread", mLock) ``` 4. **I/O线程监控**(第539-540行) ```java new HandlerChecker(IoThread.getHandler(), "i/o thread", mLock) ``` 5. **显示线程监控**(第542-544行) ```java new HandlerChecker(DisplayThread.getHandler(), "display thread", mLock) ``` 6. **动画线程监控**(第546-549行) ```java new HandlerChecker(AnimationThread.getHandler(), "animation thread", mLock) ``` 7. **Surface动画线程监控**(第551-556行) ```java new HandlerChecker(SurfaceAnimationThread.getHandler(), "surface animation thread", mLock) ``` > 所有LooperChecker都通过`withDefaultTimeout()`包装后加入`mHandlerCheckers`列表 ### 三、特殊监控添加(第558行) ```java addMonitor(new BinderThreadMonitor()); ``` 将Binder线程监控器添加到mMonitorChecker的监控队列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值