项目老代码里有个bug,关于FragmentPagerAdapter的,在Activity的onNewIntent里面调用pager中的某个fragment的方法导致空指针崩溃:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
getIntent().putExtras(intent);
if (getIntent().hasExtra("initTabByPush")) {
if (getIntent().getIntExtra("initTabByPush", 0) == CHATS) {
doctorCircleFragment.onNewIntentRefresh();
}
setTabByExtra();
}
}
其中,doctorCircleFragment就是那个空指针的罪魁祸首,它会在Activity的onCreate方法中实例化。
下面复现:
1.事故Fragment所在Activity叫做A,现在跳转到另一个Activity待命,叫做B Activity。
2.切换出去,使用清理软件清理内存。
3.切换回APP,发现屏幕白了一下,显然Application已经被回收
4.在B Activity中跳转回A Activity,触发它的onCreate方法及onNewIntent方法
5.发现事故Fragment的onCreate方法日志显示,其已经被调用
现在挺匪夷所思的,Fragment的onCreate都执行了,doctorCircleFragment不为空,掉你的方法你报还空指针?
那么只能有一种可能,就是调用的方法和onCreate的方法不是一个对象。
加入日志打印Object,果然。
因为回收之后FragmentManager会恢复之前的Fragment,所以在Activity的onCreate方法中直接创建Fragment的做法非常不靠谱,
正确的做法是先find这个Fragment是否存在,然后再决定add。否则new出来的Fragment都没add到布局上,操作它也没有什么卵用。
直接使用Fragment的时候可以find,然而FragmentPagerAdapter已经把去重这件事做了,那我们要怎么获取这个Fragment呢?
再看Adapter的代码:
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
所以我们可以通过这个实例化方法来获取被恢复的Fragment,改进后的代码如下:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
getIntent().putExtras(intent);
if (getIntent().hasExtra("initTabByPush")) {
if (getIntent().getIntExtra("initTabByPush", 0) == CHATS) {
// 解决回收空指针问题
DoctorCircleFragment f = (DoctorCircleFragment)mAdapter.instantiateItem(viewpager, CHATS);
if (f != null){
if (f.isAdded()){
f.onNewIntentRefresh();
}
}
}
setTabByExtra();
}
}
结论:在Activity中获取Fragment对象的引用的时候,一定要判断Fragment是否已经添加,否则容易引起空指针问题。
而FragmentPagerAdapter比较特殊,需要一些小把戏。
Fragment的坑还有比如add的操作是异步的,成功之后Activity无法知道,等等。不在此赘述。