Understand the behavior of startActivityForResult for Fragment and Activity
The basic startActivityForResult Activity flow
The entire startActivityForResult and onActivityResult is allowed a 2-way communication between the source activity and the destination activity. Its flow is as shown in the diagram below.
From the below flow, We can easily understand the flow of OnActivityResult from Source Activity to Destination Activity
The source activity call, startActivityForResult by sending in the intent together with the request code to Android SDK.
Android SDK then opens the activity accordingly as stated in the Intent.
Once the destination activity has finished with its job, it returns to its caller activity.
It could send the result back using setResult by sending in the resultCode and intent
Several notes here:
The resultCode is used by the destination activity to flag to its source activity what it's the status (e.g. OK, Cancel, etc).
The request code is used by the source activity to know which destination activity is returning the call.
We could see that the destination activity has no visibility of the request code.
Undocumented startActivityForResult Fragment Flow
In Fragment, we also have startActivityForResult function to call, and also onActivityResult function to override.
This raises several questions…e.g. How does it works? Is it the same as the activity’s startActivityForResult and onActivityResult? How are they related?
The short answer is, they are similar but subtly different. A frequent cause of confusion, and bug. So it’s important to understand the similarity and differences.
The fragment flow illustrated.
Below is the diagram the illustrates clearly how it all works together. The differences with activity’s startActivityForResult and onActivityResult flow are colored in RED.
Let’s more elaborate on it.
When we call Fragment’s startActivityForResult (note: NOT activity?.startActivityForResult), the creation of destination activity is the same. However, the difference is the onActivityResult.
startActivityForResult() must handle it from the
getActivity().startActivityForResult() must handle it from the
If you’re on a
fragment and you want to handle the result on the
onActivityForResult(), otherwise, if you want to handle it from the
activity of the fragment, use
When Android SDK returns onActivityResult, the request code has been altered. A 0xP0000 is appended to the original requestCode.
The P-value start from 1, and incremented for each onActivityResult is called from the same destination fragment. (If another destination Fragment is called, the P-value is reset).
e.g. if the original requestCode is 0xFF (i.e. 255), the requestCode return the first time will be 0x100FF. The second time return requestCode will be 0x200FF.
Upon returning to the source activity’s onActivityResult, it is expected that it should call super.onActivityResult, which resides in FragmentActivity (the parent of AppCompatActivity)
The onActivityResult in FragmentActivity will then extract the P value (using requestCode >> 16). The P is the index of the array where the source fragment is stored. (e.g. if requestCode is 0x200FF, then 0x200FF >> 16 = 2)
Using the P-value, it now has access to the source fragment, which is used in step 9 below.
Now, it strip away the P value from the requestCode (using requestCode & 0xffff), to get back to the original request code (e.g. if requestCode is 0x200FF, then 0x200FF & 0xffff = 0xFF)
As now it gets the original request code that the source fragment created, it could call the source fragment’s onActivityResult with the original request code.
Some basic points which want to know.
A. The behavior of Activity’s startActivityForResult is different from Fragment’s startActivityForResult
In Fragment, if we call
activity?.startActivityForResult(...), Then Fragment’s
onActivityResult will not be called automatically.
B. If we override activity’s onActivityResult, make sure we have super.onActivityResult in place
If we override source activity’s
onActivityResult, but forget to have
super.onActivityResult then Fragment’s
onActivityResult will not be called.
C. The requestCode seen in activity’s onActivityResult is different from the one fragment provided
Due to the reason, Android SDK appended
P to the
requestCode, in the activity’s
onActivityResult, we’ll never get the identical
requestCode there. This at times causes confusion to the developer when debugging the code there.
D. When an activity is restored from the state (i.e. onStateInstance != null), it should avoid recreate its fragment.
onActivityResult is called, it tried to retrieve the original fragment. This is also performed for the activity that is restored from a killed state (e.g. emulated using Don’t Keep Activity).
So if the developer recreate the Fragment every time onCreate is called (regardless if it is a restored state or not), then the original Fragment that was restored will be destroyed, and
onActivityResult of the caller, the fragment will mysteriously not be called.
This is a pitfall even for many experienced developers and app released in the field, as it is very hard to an identified bug (since it is not happening every time, and require a more complex flow to trigger).
Thanks for reading…