今天遇到一个比较常见的问题。一个fragment在点击按钮直接跳转一个新的activity的时候,报崩溃异常:fragment not attached to Activity。复现路径可能是什么样的呢?
这个问题之前在项目中也有碰到过,不过那时候忙于业务没有深入研究 当时的解决方案是,通过调用 isAdded() 判断 Fragment 是否添加到 Activity ,来避免因为上下文为空导致的崩溃 现在来研究一下下,描述:在一个 Fragment 页面中,点击按钮跳转到新的 Activity 时,报崩溃异常:Fragment not attached to Activity 问:可能路径是怎样的?
网上搜了一圈,没发现靠谱的答案,那我们就来跟踪源码,试试能不能从源码中推出来 首先,打开 Fragment 源码,路径:frameworks/base/core/java/android/app/Fragment.java 用 “not attached to Activity” 作为关键字搜索,可以发现 getResources() 、getLoaderManager() 、startActivity() 等等共计 6 处地方,都可能抛出这个错误信息 题目明确提到,是在跳转 Activity 时发生的错误,那我们直接来看 Fragment#startActivity() 方法 void startActivity(){ if (mHost == null) throw new IllegalStateException("Fragment " + this + " not attached to Activity"); } 从代码可以看到,mHost 对象为空时,就会抛出 Fragment not attached to Activity 异常
好,现在我们的问题转变为:
- mHost 对象什么时候会被赋值? 很显然,如果在赋值前调用了 startActivity() 方法,那程序必然会崩溃
- mHost 对象会被置空吗?如果会,什么时候发生? 我们都知道,Fragment 依赖 Activity 才能生存,那我们有理由怀疑: 当 Activity 执行 stop/destory ,或者,配置发生变化(比如屏幕旋转)导致 Activity 重建,会不会发生 mHost 对象置空的情况? 先来看第一个问题,mHost 对象什么时候会被赋值? 平时我们使用 Fragment 时,通常都是先 new 一个对象出来,然后再提交给 FragmentManager 去显示 我们在创建 Fragment 对象时不需要传入 mHost ,那 mHost 对象只能是 Android 系统帮我们赋值的了 得,又得去翻源码 打开 FragmentManager.java ,路径在:/frameworks/base/core/java/android/app/FragmentManager.java void moveToState(f){ if (f.mState < newState) { switch(f.mState){ case Fragment.INITIALIZING: f.mHost = mHost; // 赋值 Fragment 的 mHost 对象 } } else if (f.mState > newState) { case Fragment.CREATED: f.initState(); f.mHost = null; // mHost 对象置空 }
}
FragmentManager#moveToState() 方法中,有对 mHost 对象进行赋值、置空的操作 重点在于,理清 moveToState() 方法逻辑,什么时候执行的赋值和置空?