天天看点

FragmentPagerAdapter调用Fragment的空指针问题

项目老代码里有个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无法知道,等等。不在此赘述。