Android has a fragmentation problem. No, not that fragmentation problem. The Fragments fragmentation problem.
Way back when Honeycomb was released, Google introduced Fragments to make development for both tablets and phones a bit easier. Activities still remained the way to structure screens but now screens could have multiple fragments inside them, each with its own lifecycle.
The classic example is the List and Detail fragments - each might be alone in an Activity on phones but go together on a tablet.
And all was good. For those 10 Honeycomb developers.
In order to give all developers this neat new way of making apps, you’d need to have it backported to older Android versions. Google did that under the name of the Support Library.
The support library is a weird beast - it doesn’t just provide a backward-compatible layer, it entirely reimplements the Fragments infrastucture. Even on post-Honeycomb devices, the Fragment support still comes from the library and not the base OS. (This will be important later on)
The main problem with using Fragments for anything remotely complicated was that all communication between fragments had to go through the Activity containing them. Nested Fragments were unsupported and led to all sort of lifecycle bugs. Until API level 17, that is.
Jelly Bean 4.2 finally introduced Nested Fragments. And they were added to the Support Library, too! This was an architectural dream come true! Neat encapsulation of visual elements and business logic, no messy Activities, all is great!
Yeah, about that..
Terminology: root fragments are the fragments that were added to the Activity’s FragmentManager. Anything else is a nested fragment. Same rules for the fragment managers
The bug: If you, like me, are working on a relatively modern application, you want your UI to be smoothly animated. The FragmentManager allows you to set transitions for any transaction. Except, the exit animations will always cause the nested fragments to disappear when the animation starts.
The reason: Fragments have a “nested” lifecycle. That is, the nested fragments are stopped before their parent. Because the root fragment manager doesn’t know about them, their View hierarchy is yanked before the animation starts. The root fragment will still animate correctly.
The fix: See the StackOverflow question for a really hacky workaround. Essentially, you cache the visible state of the root fragment and set this cache as a background. Unfortunately, it does cause a complete redraw (along with possible layout issues)
Backround: Fragments can request to have their instance retained and not recreated from scratch when the parent Activity undergoes a configuration change (e.g., screen orientation change)
The bug: Nested fragments inherit the retain instance status of their parent fragment.
The reason: I’ve not investigated this in detail, just noted it and calmly put down the lock of hair I’d pulled off of my head.
The fix: None that I’m aware of.
Discussion: This might seem like a tiny little thing but it has deep consequences. I prefer to have all fragments recreated as it ensures that they can serialise their state correctly (especially ones that have views). However, I also have view-less fragments for things like network traffic and interaction with external libraries where you might set a listener that needs to be retained. Such fragments will not be retained if they are nested under something with a view hierarchy. The solution I’m currently employing uses static instances, weak references to the fragment and manual logic to redeliver the callbacks. It’s messy.
This one is the most troubling, actually. You are very likely to hit it, if
you ever start activities from nested fragments. It can strike in unexpected ways, for example
when opening a
Session via the Facebook SDK.
The bug: Activity results will be swallowed up or straight-out misdelivered for any activity started from a nested fragment.
The reason: The support library modifies the
requestCode to include a
fragment index in the top 16 bits. This index is relative to the
fragment’s fragment manager, which might be nested. However, the
activity assumes that the index is within its own fragment manager, so
will misdeliver the result. Fragments will also not propagate the result to any
of their children.
The solution: Propagate results manually from any root fragment to its children.
The twist: The native implementation works flawlessly. Better, actually, since the Activity is never notified. Thanks, Google! I think.
I hope you enjoyed this little gallery. I certainly had fun making it.