1. 程式人生 > >Fragment的狀態儲存startActivityForResult是哪個類的方法,在什麼情況下使用?

Fragment的狀態儲存startActivityForResult是哪個類的方法,在什麼情況下使用?


Fragment呼叫startActivityForResult ---> HostCallbacks . onStartActivityFromFragment ---> FragmentActivity . startActivityFromFragment 

FragmentActivity的startActivityFromFragment方法

    public void startActivityFromFragment(Fragment fragment, Intent intent,
            int requestCode, @Nullable Bundle options) {
        mStartedActivityFromFragment = true;
        try {
            if (requestCode == -1) {
                ActivityCompat.startActivityForResult(this, intent, -1, options);
                return;
            }
            checkForValidRequestCode(requestCode);
            int requestIndex = allocateRequestIndex(fragment);
            ActivityCompat.startActivityForResult(
                    this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
        } finally {
            mStartedActivityFromFragment = false;
        }
    }

首先將FragmentActivity父類BaseFragmentActivityJB 的 mStartedActivityFromFragment 設定為 true;
然後根據requestCode的值判斷,呼叫startActivity預設這裡的requestCode 為 -1 ,如果呼叫startActivityForResult則不會走這步;
下一步通過checkForValidRequestCode會查驗requestCode是否正確

    static void checkForValidRequestCode(int requestCode) {
        if ((requestCode & 0xffff0000) != 0) {
            throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
        }
    }

如果指定的requestCode大於65535,則會報異常.

下一步呼叫allocateRequestIndex方法得到請求佇列中的索引

 // Allocates the next available startActivityForResult request index.
    private int allocateRequestIndex(Fragment fragment) {
        // Sanity check that we havn't exhaused the request index space.
        if (mPendingFragmentActivityResults.size() >= MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS) {
            throw new IllegalStateException("Too many pending Fragment activity results.");
        }

        // Find an unallocated request index in the mPendingFragmentActivityResults map.
        while (mPendingFragmentActivityResults.indexOfKey(mNextCandidateRequestIndex) >= 0) {
            mNextCandidateRequestIndex =
                    (mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;
        }

        int requestIndex = mNextCandidateRequestIndex;
        mPendingFragmentActivityResults.put(requestIndex, fragment.mWho);
        mNextCandidateRequestIndex =
                (mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;

        return requestIndex;
    }

如果佇列中超過了65535-1  那麼報引數錯誤,然後得到在等待獲取結果的佇列中的索引,indexOfKey >=0 代表已經存在了,只能將mNextCandidateRequestIndex+1;不存在則賦值給requestIndex 然後返回,並且將mNextCandidateRequestIndex+1 給下一個請求做準備.

下一步呼叫
ActivityCompat . startIntentSenderForResult(this, intent,((requestIndex + 1) << 16) + (requestCode & 0xffff), fillInIntent,flagsMask, flagsValues, extraFlags, options);
(requestIndex + 1) << 16  代表將請求的索引值左移16位,在較高16位中儲存當前的索引值
requestCode & 0xffff  就是requestCode,為了遮蔽二進位制位,置位0

    public static void startIntentSenderForResult(Activity activity, IntentSender intent,
            int requestCode, Intent fillInIntent, int flagsMask, int flagsValues,
            int extraFlags, @Nullable Bundle options) throws IntentSender.SendIntentException {
        if (Build.VERSION.SDK_INT >= 16) {
            ActivityCompatJB.startIntentSenderForResult(activity, intent, requestCode, fillInIntent,
                    flagsMask, flagsValues, extraFlags, options);
        } else {
            activity.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
                    flagsValues, extraFlags);
        }
    }

因此這裡呼叫startIntentSenderForResult方法的引數requestCode實際上是 ((requestIndex + 1) << 16)  + (requestCode & 0xffff )  而不是之前的requestCode。
 

接著呼叫ActivityCompatJB . startIntentSenderForResult方法(這裡只拿API>=16舉例)

    public static void startIntentSenderForResult(Activity activity, IntentSender intent,
            int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
            Bundle options) throws IntentSender.SendIntentException {
        activity.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
                flagsValues, extraFlags, options);
    }

最後呼叫Activity的 startIntentSenderForResult 方法,ActivityManagerNative.getDefault().startActivityIntentSender 真正啟動一個Activity。

然後我們檢視FragmentActivity中接收setResult的值的方法onActivityResult

 /**
     * Dispatch incoming result to the correct fragment.
     */
    @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);
    }

首先將requestCode值右移16位,得到他在等待處理Fragment資料佇列的索引,由上面的 ActivityCompat . startIntentSenderForResult方法引數得知,requestCode是左移了16位,
那麼這裡就可以根據requestIndex的值判斷是Fragment呼叫了startActivityForResult還是Activity,如果requestIndex不為0代表是Fragment觸發,下面得到 targetFragment 然後可以通過targetFragment的onActivityResult方法得到返回值;如果requestIndex為0代表是Activity呼叫了startActivityForResult,那麼直接返回的是Activity的onActivityResult方法。

需要注意:
Fragment 和 Activity 都有startActivityForResult方法,切記不要呼叫錯誤。如果發現Fragment 的onActivityResult拿不到資料,
而FragmentActivity拿到了資料,並且requestCode與Fragment中用到的requestCode不一致,那麼一定是在Fragment中呼叫了FragmentActivity的startActivityForResult方法。
原始碼就可以看出requestCode不一致的原因了,使用Fragment發起的請求,因為一個Activity可以包含多個Fragment,而且都可以發起請求,那麼會使用一個容器來儲存發起請求的Fragment和它對應的requestCode,mPendingFragmentActivityResults就是這個容器,發起請求的時候將在容器中的index和requestCode算在一起,作為最終的requestCode。
FragmentActivity 和 它的內部類 HostCallbacks 分別展現了兩個startActivityForResult方法的呼叫。FragmentActi本身是有startActivityForResult方法,依附它的Fragment也有startActivityForResult方法,那麼放在HostCallbacks裡,這就體現了HostCallbacks是一座橋樑,連結了Fragment和FragmentActivity之間的通訊。