Android. Support library. Nested fragments and startActivityForResult()
This post is dedicated to nested Fragments from the support library and to their big issue.
Fragment has the following methods: startActivityForResult() and onActivityResult(). The first one just delegates the call to FragmentActivity.startActivityFromFragment(), and the second one is called from FragmentActivity.onActivityResult().
If this behavior is just a wrapper around Activity, then how is the result delivered to the Fragment instance?
All the magic is hidden in the method’s argument requestCode. FragmentActivity allows only the lower 16 bits to be used for external purposes. The higher 16 bits are used to store the private index of the Fragment inside FragmentManager:
// FragmentActivity.java
/**
* Modifies the standard behavior to allow results to be delivered to fragments.
* This imposes a restriction that requestCode be <= 0xffff.
*/
@Override
public void startActivityForResult(Intent intent, int requestCode) {
if (requestCode != -1 && (requestCode&0xffff0000) != 0) {
throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
}
super.startActivityForResult(intent, requestCode);
}
/**
* Called by Fragment.startActivityForResult() to implement its behavior.
*/
public void startActivityFromFragment(Fragment fragment, Intent intent,
int requestCode) {
if (requestCode == -1) {
super.startActivityForResult(intent, -1);
return;
}
if ((requestCode&0xffff0000) != 0) {
throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
}
super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
}
FragmentActivity replaces requestCode with a modified one. After that, when onActivityResult() is invoked, FragmentActivity parses the higher 16 bits and restores the index of the original Fragment. Look at this scheme:
Well, this dirty bitwise hack allows us to start an Activity for result from a Fragment and handle the answer inside the Fragment. So, what’s the problem?
If you have a few fragments at the root level, there are no problems. But if you have nested fragments, for example a Fragment with a few tabs inside a ViewPager, you will definitely run into a problem (or already have).
Method Fragment.onActivityResult() will not be called for nested fragments.
// FragmentActivity.java
/**
* 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);
}
This happens because only one index is stored inside requestCode: the index of the Fragment inside its FragmentManager. When we use nested fragments, there is a child FragmentManager, which has its own list of Fragments. So, it’s necessary to save the whole chain of indices, starting from the root FragmentManager.
How can we fix this? Only 16 bits of requestCode are available to us. We tried to skip the default behavior completely and use all 32 bits, but FragmentActivity throws an exception inside its methods (see the first code snippet above) if you try to use the higher 16 bits yourself. We created an issue on the bug tracker which describes this situation.
Until it is fixed, all we can do is use the lower 16 bits to store both the chain of fragments and the externally used requestCode.
A real app doesn’t have that many Fragments. A typical app has from 1 to 4 Fragments on one screen. An app rarely has a deep structure of nested fragments — usually just 1 or 2 levels of nesting with ViewPager. An int variable is far too big to store such an index (one int per index). So, we can significantly reduce the number of precious bits used to store indices.
For example, with 3 bits we can store numbers from 0 to 7. Reserving 3 levels of depth, we spend just 9 of the 16 allowed bits. The remaining 7 bits (values from 0 to 127) are available for external use.
You can clone and check this solution on GitHub. If you check out “Initial commit”, you will see the problem described above. In the HEAD of the repository the problem is fixed.
Of course, this solution adds more restrictions:
- We can’t use a requestCode greater than 127.
- We can’t have more than 3 levels of nested fragments.
- We can’t use more than 7 fragments at one level.
But look at real apps — these limits are quite acceptable!
Well, if you’re reading this, the post was interesting for you. Thanks so much!
Sources from this post are available on GitHub.


