1. 程式人生 > >Android中在app被系統釋放後,重新回到前臺時,重建Activity出現Fragment重疊現象

Android中在app被系統釋放後,重新回到前臺時,重建Activity出現Fragment重疊現象

android 開發中Frgment應用場景非常廣泛,應用Fragment是必須使用FragmentActivity,有時候會出現這樣一種情況,在應用退到後臺後,系統會在記憶體不足時將應用回收掉,再次啟動應用會出現Activity中的Fragment重疊現象,其實出現問題的原因是這樣的,在回收應用回收前FragmentActivity會呼叫onSaveInstanceState(Bundle outState) 方法,儲存當前的Fragment的狀態。

 @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mPendingFragmentActivityResults.size() > 0) {
            outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);

            int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
            String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
            for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
                requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
                fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
            }
            outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
            outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
        }
    }

我們再次開啟應用FragmentActivity會重新建立走onCreate方法。在FragmentActivity的onCreate方法如果不處理,會恢復之前儲存的Fragment狀態,同時我們又在onCreate中重新初始化了Fragment,這樣就會出現Fragment兩次被新增到Activity的情況。

 protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);

        super.onCreate(savedInstanceState);

        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            mFragments.restoreLoaderNonConfig(nc.loaders);
        }
        if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);

            // Check if there are any pending onActivityResult calls to descendent Fragments.
            if (savedInstanceState.containsKey(NEXT_CANDIDATE_REQUEST_INDEX_TAG)) {
                mNextCandidateRequestIndex =
                        savedInstanceState.getInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG);
                int[] requestCodes = savedInstanceState.getIntArray(ALLOCATED_REQUEST_INDICIES_TAG);
                String[] fragmentWhos = savedInstanceState.getStringArray(REQUEST_FRAGMENT_WHO_TAG);
                if (requestCodes == null || fragmentWhos == null ||
                            requestCodes.length != fragmentWhos.length) {
                    Log.w(TAG, "Invalid requestCode mapping in savedInstanceState.");
                } else {
                    mPendingFragmentActivityResults = new SparseArrayCompat<>(requestCodes.length);
                    for (int i = 0; i < requestCodes.length; i++) {
                        mPendingFragmentActivityResults.put(requestCodes[i], fragmentWhos[i]);
                    }
                }
            }
        }

        if (mPendingFragmentActivityResults == null) {
            mPendingFragmentActivityResults = new SparseArrayCompat<>();
            mNextCandidateRequestIndex = 0;
        }

        mFragments.dispatchCreate();
    }

通過上面的簡單分析就可基本上對問題的解決就有一個大致的解決思路啦,下面我提供了自己認為的解決方案

方案一:
在onCreate方法方法中判斷savedInstanceState變數是否為空,如果不為空就是認為是重建的,我們給savedInstanceState.putParcelable(FRAGMENTS_TAG, null);這樣系統就不會恢復savedInstanceState儲存的Fragment了,也就不會出現重複新增的問題了。這處理的問題比較簡單,不易出現錯誤,但是重建後的狀態會回收之前的狀態不一樣,會影響使用者體驗。

 protected void onCreate(@Nullable Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            //activity 回收恢復時,重新生成 fragment。
            savedInstanceState.putParcelable(FRAGMENTS_TAG, null);
        }
        super.onCreate(savedInstanceState);
     
 }

方案二:
我同樣要進行判斷在onCreate方法方法中判斷savedInstanceState變數是否為空,如果不為空就認為是重建的,不執行Fragment的建立工作,為空的情況才執行Fragment的建立工作。使用種方案會使重建後和重建前的效果一樣,使用者體驗好,但是對於比較複雜的Fragment可能會增加錯誤的風險。

protected void onCreate(@Nullable Bundle savedInstanceState) {

        if (savedInstanceState == null) {
            intFragment();
        }
}

方案三:
就是在回收之前不對狀態進行儲存,我們重寫onSaveInstanceState 並且不呼叫Fragment的onSaveInstanceState方法。用這樣方案的確可以解決上面的問題,但是這樣會讓onSaveInstanceState 方法完全失去意義,就儲存不了回收之前的任何資訊了。

 @Override
    protected void onSaveInstanceState(Bundle outState) {
//        super.onSaveInstanceState(outState);
    }

以上的我認為的三種可以行的解決方案,應該還有更好的解決方法等著我去發現,如果後期有發現新的方案和有改進方法我還會進行更新。