Nested PhotoFragments for Android

Nested fragments were officially introduced in Android 4.2 and have been known for their buggy nature in the support library ever since. Recently I decided to play around with them and discovered several issues in conjunction with using Bitmaps that were kinda surprising and took a while to work-around – anyway the full code is up on GitHub if you’re interested but essentially this project lets you create nested fragments that each show a bitmap taken with the camera or retrieved from gallery. Here’s just an quick visual:

Nested Fragment issues with Bitmaps in Support Library, Part I

As a developer you should always be prepared for the inevitability that your favorite user will do something dumb with your app. In Android something as seemingly benign as rotating the screen will destroy then recreate your Activity and reinstantiate all associated objects. Prior to 3.0 this behavior didn’t extend to bitmaps, so unless you manually recycled images yourself they would still linger around in memory long after their host activity had been destroyed and a new one created. If a user rotated their device a few times (maybe rolling down a hill while taking selfies?) and you’d somehow forgotten to call recycle() on those bitmaps prior to the activity being destroyed on every orientation change, then those leaked images would build up and eventually crash the app, ultimately leaving you with an angry review from your user, his poses lost to all history.

Google addressed bitmaps in 3.0 so that they’re automatically cleaned up and explicitly calling recycle() yourself should no longer be necessary. And in most cases it isn’t; we should now all be able to safely take rolling selfies until either our disk storage or the hill runs out. Fortunately even with this improvement Google had the foresight not to deprecate the method, just in case there ever came a time when it was needed.

So where am I going with all this, and what does any of this have to do with nested fragments?! Well Fragments themselves have their own way of retaining associated objects after an orientation change so those objects don’t continually need to be reinstantiated – simply call setRetainInstance(true) in your fragment’s onCreate() and be done with it. Alas when it comes to nested fragments, life couldn’t be so easy. Due to a bug in the support library, setRetainInstance() doesn’t work for nested fragments; and if you try to use them you’ll see these bold words in the log staring back at you:

FYI: Google has a longstanding ticket on this issue that’s still open at time of this writing if you want to check it out further.

A ‘fragement’ you say? Well no problem, let’s just go back to our original answer and manually call recycle() on those bitmaps prior to the fragment’s destruction, then we can simply recreate those images later, after our fragment is recreated. So you call recycle(), and then sit back and watch in abject horror as the memory monitor shows these zombie images continue to persist, thriving even upon every subsequent rotation, until.. CRASH!!

Somehow this nested fragment, which due to a bug cannot retain anything within its purview, is actually retaining its bitmaps AFTER we manually call recycle on them! But how can this be? As it turns out, this is partly due to Google enabling full hardware accelerated drawing in 4.0 (the feature was actually added back in Honeycomb but back then you needed to manually turn it on inside your app’s manifest, from 4.0 onward it’s enabled silently by default). Even though this has absolutely no effect on bitmaps in activities and single fragments, which behave as expected, it does manage to prevent recycling bitmaps in nested fragments.

Anyways, if you ever find yourself in this predicament, first of all you have my extreme sympathies, but more importantly rather than disabling hardware acceleration for your entire app all you should do is just disable it for the ImageViews within your nested fragment.

And that’s it! You can now call recycle() on those bitmaps and they will actually recycle! Anyway there were several other issues I uncovered with nested fragments, so I’ll do a Part II when I have time.