Android中OnActivityResult()不被Fragment調用的解決辦法及源碼分析

前言

在項目用到Fragment嵌套Fragment的結構,出現子Fragment的OnActivityResult()不被回調的問題,參考鴻軍大神的解決方案得以解決。後來由於項目需要對SDK版本進行升級,發現23版本以後,這個問題谷歌大神們已經修復了這個bug!良心啊!遂斗膽在此對源碼分析一下,以作記錄!

Fragment的OnActivityResult()源碼分析

Fragment的OnActivityResult()實現的原理還是將數據傳到Fragment依賴的Activity中,然後在Activity中將數據分發到其下的Fragment中。
首先再次聲明這是講SDK版本升級到23以後才解決的,23版本之前的還需要參考鴻軍大神的解決方法。
以下是23之前的源碼:

/**
 * Dispatch incoming result to the correct fragment.
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    mFragments.noteStateNotSaved();
    int index = requestCode>>16;
    if (index != 0) {
        index--;
        if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
            Log.w(TAG, "Activity result fragment index out of range: 0x"
                    + Integer.toHexString(requestCode));
            return;
        }
        ***Fragment frag = mFragments.mActive.get(index);***
        if (frag == null) {
            Log.w(TAG, "Activity result no fragment exists for index: 0x"
                    + Integer.toHexString(requestCode));
        } else {
            frag.onActivityResult(requestCode&0xffff, resultCode, data);
        }
        return;
    }

    super.onActivityResult(requestCode, resultCode, data);
}

以下是23以後的源碼:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    mFragments.noteStateNotSaved();
    int requestIndex = requestCode>>16;
    if (requestIndex != 0) {
        requestIndex--;

        ***String who = mPendingFragmentActivityResults.get(requestIndex);***
        mPendingFragmentActivityResults.remove(requestIndex);
        if (who == null) {
            Log.w(TAG, "Activity result delivered for unknown Fragment.");
            return;
        }
        ***Fragment targetFragment = mFragments.findFragmentByWho(who);***
        if (targetFragment == null) {
            Log.w(TAG, "Activity result no fragment exists for who: " + who);
        } else {
            targetFragment.onActivityResult(requestCode&0xffff, resultCode, data);
        }
        return;
    }

    super.onActivityResult(requestCode, resultCode, data);
}

不同點我已經用加粗標註了,下面我就着重分析一下加粗處的源碼。
其中mPendingFragmentActivityResults是SparseArrayCompat 數據格式。
擴展一下:SparseArrayCompat()其實是一個map容器,它使用了一套算法優化了hashMap,可以節省至少50%的緩存。
缺點但是有侷限性只針對下面類型:key: Integer; value: object。
針對mPendingFragmentActivityResults官方給出以下解釋:

A map from request index to Fragment “who” (i.e. a Fragment’s unique identifier). Used to keep track of the originating Fragment for Fragment.startActivityForResult(…) calls, so we can dispatch the onActivityResult(…) to the appropriate Fragment. Will only contain entries for startActivityForResult calls where a result has not yet been delivered.

從解釋中我們可知道通過mPendingFragmentActivityResults我們可以得到唯一的Fragment標識.

以下才是重中之重,關於mFragments.findFragmentByWho(who)方法的分析。

從源碼中可以知道mFragments的類型爲FragmentController。FragmentController下的findFragmentByWho(String who)方法源碼爲:

/**
 * Returns a fragment with the given identifier.
 */
@Nullable
Fragment findFragmentByWho(String who) {
    return mHost.mFragmentManager.findFragmentByWho(who);
}

繼續看,mHost的類型爲我們熟悉的FragmentManager,其下有重中之重的遞歸方法:

public Fragment findFragmentByWho(String who) {
    if (mActive != null && who != null) {
        for (int i=mActive.size()-1; i>=0; i--) {
            Fragment f = mActive.get(i);
            if (f != null && (f=f.findFragmentByWho(who)) != null) {
                return f;
            }
        }
    }
    return null;
}

當同時滿足Fragment != null並且Fragment.findFragmentByWho(who)不爲空時,返回的就是需要接收數據的Fragment了。
繼續看源碼,這次就看Fragment下面的findFragmentByWho(String who)了:

Fragment findFragmentByWho(String who) {
    if (who.equals(mWho)) {
        return this;
    }
    if (mChildFragmentManager != null) {
        return mChildFragmentManager.findFragmentByWho(who);
    }
    return null;
}

從這個方法中我們可以看到,如果當前的Fragment已經是你需要找到的那個,就直接返回,如果還不是,那就繼續在它的子類中繼續尋找,即mChildFragmentManager.findFragmentByWho(who),繼續調用FragmentManager下的findFragmentByWho(String who)方法。通過調用遞歸方法,最終找到目標Fragment並返回。

總結

通過以上源碼分析,我們可以知道,谷歌已經解決了子Fragment不能調用OnActivityResult()回調方法的bug,我們再也不用自己再自己解決這個問題,感覺好高興!

另附低版本SDK解決的方法:
可以參考鴻軍大神的博文:http://blog.csdn.net/shuaihj/article/details/46663109

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章