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
}