This article was first published on the public account “AntDream”. You are welcome to search “AntDream” on wechat or scan the QR code at the bottom of this article to follow it. We will make progress every day

Call startActivityForResult in Fragment there are several situations to note when calling startActivityForResult in Fragment

  1. Only the onActivityResult of the parent Activity will be called. The onActivityResult of the Fragment will not be called
  2. Initiate the startActivityForResult call. Both the onActivityResult of the current Fragment and the onActivityResult of the parent Activity are called
  3. Only the parent Activity and onActivityResult of the parent Fragment will be called. The onActivityResult of the current Fragment will not be called.

If onActivityResult is overridden in the parent activity, super.onActivityResult() must be added to the parent activity’s onActivityResult.

It all boils down to this: where you start a call, you end up there.

Source code analysis

Fragment directly called in FragmentstartActivityForResult

(1) InitiatestartActivityForResultcall

This case calls directly to the Fragment’s startActivityForResult method

//Fragment.class
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    if (mHost == null) {
        throw new IllegalStateException("Fragment " + this + " not attached to Activity");
    }
    mHost.onStartActivityFromFragment(this /*fragment*/, intent, requestCode, options);
}
Copy the code

The corresponding is the father of fragments FragmentActivity mHost, so FragmentActivity startActivityFromFragment method will be called to the father

//FragmentActivity.class
public void startActivityFromFragment(Fragment fragment, Intent intent,
        int requestCode, @Nullable Bundle options) {
    mStartedActivityFromFragment = true; Try {// Usually the requestCode will not be -1, so will not goifinsideif (requestCode == -1) {
            ActivityCompat.startActivityForResult(this, intent, -1, options);
            return; CheckForValidRequestCode (requestCode); RequestIndex(Fragment) mWho int requestIndex = allocaterrequestindex (Fragment); // Initiate startActivityForResult, Here requestIndex and requestCode associated ActivityCompat. StartActivityForResult (this intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options); } finally { mStartedActivityFromFragment =false; }}Copy the code

Each Fragment has a unique identifier field inside who, All call startActivityFromFragment method in FragmentActivity fragments requestCode and who through key – value stored in the mPendingFragmentActivityResu LTS variable

private int allocateRequestIndex(Fragment fragment) { ... int requestIndex = mNextCandidateRequestIndex; / / will requestIndex and fragments mWho mPendingFragmentActivityResults preservation. The put (requestIndex, fragments. MWho); mNextCandidateRequestIndex = (mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;return requestIndex;
}
Copy the code

The requestIndex and Fragment mWho variable are related by the equestIndex method allocateRequestIndex

In the above startActivityFromFragment method call ActivityCompat startActivityForResult method of startup Activity when the requestIndex associated with requestCode again

The onActivityResult method of the Fragment can then be called using the Fragment’s onActivityResult method based on the requestCode

Finally, take a look at ActivityCompat’s startActivityForResult method

public static void startActivityForResult(@NonNull Activity activity, @NonNull Intent intent,
        int requestCode, @Nullable Bundle options) {
    if (Build.VERSION.SDK_INT >= 16) {
        activity.startActivityForResult(intent, requestCode, options);
    } else{ activity.startActivityForResult(intent, requestCode); }}Copy the code
(2)onActivityResultMethods the callback

With breakpoint debugging, we see that the onActivityResult of the parent Activity is called back first, that is, the onActivityResult of our FragmentActivity

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { mFragments.noteStateNotSaved(); int requestIndex = requestCode>>16; //requestIndex = 0 indicates that no Fragment has initiated the startActivityForResult callif(requestIndex ! = 0) { requestIndex--; / / according to requestIndex for fragments of the who variable String = mPendingFragmentActivityResults. Who get (requestIndex); mPendingFragmentActivityResults.remove(requestIndex);if (who == null) {
            Log.w(TAG, "Activity result delivered for unknown Fragment.");
            return; } / / then according to the who variables for target fragments fragments targetFragment = mFragments. FindFragmentByWho (who);if (targetFragment == null) {
            Log.w(TAG, "Activity result no fragment exists for who: " + who);
        } else{/ / the last call fragments onActivityResult targetFragment. OnActivityResult (requestCode & 0 XFFFF, the resultCode, data); }return; }... super.onActivityResult(requestCode, resultCode, data); }Copy the code

As you can see from the above method, the onActivityResult method in FragmentActivity already handles the Fragment’s startActivityForResult call.

The onActivityResult() method must be super.onActivityResult(), Otherwise the onActivityResult method in your Fragment has no way to call back.

This is why 2 and 3 points mentioned at the beginning of the article need to be paid attention to

GetParentFragment initiates the call

This usually happens when you have nested multiple layers of fragments

The procedure for getParentFragment to initiate a call is similar to the procedure above, except that the call is initiated by the parent Fragment of the current Fragment. The onActivityResult method of the parent Activity and the onActivityResult method of the parent Fragment are called back.

So if you want to listen for callbacks from the onActivityResult method in a child Fragment, don’t use this method

The getActivity method initiates the call

The onActivityResult method of the parent Activity is called directly

//FragmentActivity.class
@Override
    public void startActivityForResult(Intent intent, int requestCode) {
        // If this was started from a Fragment we've already checked the upper 16 bits were not in // use, and then repurposed them for the Fragment's index.
        if(! mStartedActivityFromFragment) {if(requestCode ! = -1) { checkForValidRequestCode(requestCode); } } super.startActivityForResult(intent, requestCode); }Copy the code

As you can see from the source code, this method does not end up calling the Fragment’s onActivityResult method

conclusion

Calling startActivityForResult in your Fragment and listening on onActivityResult is a common way to use your Fragment, but if you are not careful, you will fall into a trap. For example, the Fragment does not receive the onActivityResult callback because the Activity’s onActivityResult method does not call super.onActivityResult().

Finally, summarize the application steps of several scenarios:

(1) An Activity contains a layer of fragments that need to listen for onActivityResult to return the result
  • Called directly in the FragmentstartActivityForResultmethods
  • If overridden in the parent ActivityonActivityResult, you need to make sure it is calledsuper.onActivityResult()methods
  • Implement the onActivityResult method in Fragment to listen for callback results
(2) An Activity has multiple fragments nested inside it. In the Fragment, you need to listen for onActivityResult to return the result
  • This is the same as above, we can see from the source code above, which Fragment is initiatedstartActivityForResultAs long as the parent ActivityonActivityResultMethod calledsuper.onActivityResult()Method in FragmentonActivityResultThe method will be called back

Call startActivityForResult() instead of getActivity().startActivityForResult() Don’t use getParentFragment().startActivityForResult() either, unless you know why!


Welcome to follow my wechat public number, and make progress with me every day!Copy the code