天天看點

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無法知道,等等。不在此贅述。