Double Negative

Software, code and things.

Nested fragments not retained in latest support library releases

Since version 17 of the Android API it has been possible to nest fragments within your applications. Using the support library that the Android team provide, you can utilize this functionality from version 11 onwards.

There is however a bemusing issue in the latest versions of the support library which mean that nested fragments dont function as one would expect.

As outlined in this issue, versions 20 and 21 of the support library do not retain nested fragments correctly on configuration changes. This can be quite a difficult issue to diagnose if you are not expecting it (which you shouldn't be). I stumbled upon it because a nested map was disappearing on orientation change.

A duplicate issue more explicitly states the cause: "The issue is that Fragments now always null out their mChildFragmentManager property, so when they are re-attached to an Activity, a new child FragmentManager is always created, thus losing the old nested Fragments."

I wanted to see for myself so had a look at the source where you can see that the mChildFragmentManager is set to null.

Within the comments on the issue there is a link which proposes a solution.

The proposed solution is not however particularly clean or clear. As such I have created a gist with the solution I am using.

It works as follows:

  • The first time we load a fragment that extends from this base class, we save a reference to the child fragment manager.

  • On orientation change the saved instance variable is retained.

  • When the fragment is reattached post orientation change, if the variable is non null we use reflection to assign its value to the property which the support library uses.

I find it really quite bemusing that the Android team have not commented on this.. an issue that has been present for 8 months. I am also suprised that so few people have had anything to say on the matter.

Considerations

It should be noted that this fix isn't guaranteed to continue to work. Hopefully Google will fix this in the next support library release but if they don't it is theoretically possible that they could change the variable name used for the child fragment manager (although extremely unlikely).

Reflection is less than ideal but the methods we would need to override/the variables we need to access to fix this any other way are package private so this is the best we will get for now.

Thanks

Thanks of course to all who have commented on/investigated the issue. Hopefully this post provides a little more exposure for anyone else experiencing it and looking for a solution.

Simple Android Dependency Injection

Whilst I am aware of, and appreciative of the merits of both Roboguice and Dagger, I felt that they were not suitable for what I had initially intended to be a simple application.

That is not to say that they are complex or difficult to use but rather I didn't want my application to require another 3rd party library to function correctly. Especially given that my investigation into dependency injection (in Android) had only come about as a result of wanting to unit test a complete and working app.

The answer that I ended up with came about based on my own knowledge and research as well as general trial and error. Hopefully the information below will help someone else.

Constructor injection

My first thought was constructor injection. It seemed logical that injecting the service(s) on which my activity depended would be the easiest way to allow for mocking.

Unfortunately the Android frameworks do not allow for direct instantiation of activities, and as such injecting a dependency in the constructor is not easy enough to be worth doing.

You can however instantiate fragments directly, and as such constructor injection does have a part to play in my overall solution.

Fragments must have a constructor without parameters. That said, a common pattern for constructing fragments with parameters is to use a static factory method which creates the fragment instance. An example of this pattern can be seen here.

I decided that for the purposes of testing my fragments I would inject a dependency factory (a class which lazily creates instances of my fragments dependencies) into my static factory methods.

The benefits of this are twofold:

  • Lazy loading. We do not waste memory instantiating dependencies up front. We create them as/when we need them.

  • Dynamic instantiation parameters. Whilst you might know what dependencies a fragment relies upon, at the point of injection you may well not know the parameters than your dependency requires to instantiate itself correctly. By using a dependency factory we can build our dependency with the correct parameters when we known what they are.

One small problem

There is one small 'problem' with this method (unfortunately).

The example linked above demonstrates passing in simple values and setting them in a bundle - the fragments arguments bundle. What I want to do is pass in an instance of a (potentially very complex) dependency factory. Are there any extra considerations?

The example above sets this data in the arguments bundle because it is this bundle that the Android framework saves/restores when the fragment is recreated. The Android framework recreates fragments by calling their no parameter constructor. As such if you do not save parameters in the arguments bundle they will not be present when the fragment is recreated (for example on screen rotation).

The resolution for this is to either implement the Parcellable interface in your dependency factory and save your instance in the bundle (this is pretty advanced stuff) OR to retain your fragments state on recreation.

When I instantiate my fragment, I set the dependency factory as a property of said fragment. I can then access it to request a dependency anywhere within.

By using setRetainInstance(true) within my fragments 'onCreate' method, my fragments state (including its instance properties) are maintained by the Android framework. Likewise, if you replace a fragment (adding the transaction to the backstack) and then pop the backstack the instance variables are also retained.

For more information on retained state or for general clarification, I suggest you have a play with my fragment demo from a previous post. You can set your dependency factory as an instance variable, rotate your screen and still have access to that same instance - the arguments bundle is not required.

What about the activities?

As mentioned above, constructor injection does not work with activities.

The only simple way to inject a dependency into an activity is with setter injection. The problem with this however is that (as mentioned above) the instantiation of, and lifecycle of an activity is pretty well defined by the Android framework. There is no opportunity to set your dependency before it is needed.

Even if it were possible one would certainly want to avoid "two-step construction" (an antipattern) - there is a certain irony to making your code patently worse whilst trying to make it testable :)

With no obvious or apparent resolution to my issue it got me thinking about the environment in which I want to test. I have no interest in using Robolectric for the same reasons that I am avoiding Dagger. All I want is a nice simple ActivityUnitTestCase. Eureka !

A call to startActivity within an ActivityUnitTestCase creates an isolated test environment whereby only the onCreate method of the activity under test is executed. As such I can inject my dependency factory before it is needed.

What about two step construction you say? Well.. whilst my activities do require a dependency factory to function, the default factory is not injected but rather hard coded. The setter is only used for cases where we want to override the default.. for example testing.

Whilst this does mean modifying your code to facilitate testing (which is admittedly less than ideal), I am happy (personally) with the tradeoff to make my activities testable.

If you really want to, you could subclass your activities within your test package for the purposes of testing.

So where are we?

At this point I have outlined how you can make dependencies available to both activities and fragments in a way which allows for them to be swapped out (mocked) for testing.

It is not a perfect solution but it is simple, and it is easy to implement.

If you have any questions, I would be happy to answer them.

Running on the main thread with ActivityUnitTestCase

I was attempting to test a method which sets a photo inside an ImageView using Picasso when I stumbled upon an error. The error outputted to logcat was "Method call should happen from the main thread". It was thrown by Picasso, and it was causing my test to fail.

I searched the web to try and find guidance on resolving this error and found this issue in the Picasso github repo.

The developer Jake Wharton outlines the reasons why the exception is thrown, and why the constraint cannot be relaxed.

The issue reporter follows up stating how they resolved the issue by changing from a subclass of ActivityTestCase to a subclass of InstrumentationTestCase. In my case however, I am writing a unit test and the isolation that ActivityUnitTestCase offers me is much closer to what I want to be working with than the environment in which an InstrumentationTestCase runs.

I played around with.. everything.

I stumbled upon this StackOverflow question which proposes a solution which may well resolve your issues if you are testing a unit which only interacts with the UI thread.

Unfortunately my setup for unit testing requires a call to waitForIdleSync which the documentation explicitly states can "not be called from the main application thread". As such universally wrapping my text execution in a runnable as suggested was not suitable.

I then stumbled upon this answer which unfortunately is a little vague and unclear. It did however get me thinking.

Handlers by default run on the thread on which they are created. I considered simply running the part of the code that needed to be executed on the main thread within a call to runOnUiThread. This however was not working.. I believe because under the hood this method uses a Handler.

After a bit more thinking, Googleing, and testing I found a fix and an amazing Stack Overflow question which clarified and cleared up nearly all of my thoughts on the matter (read through all the answers, the comments etc - it is really interesting stuff).

HPP's comment which has now become an answer was the solution:

new Handler(Looper.getMainLooper()).post(new Runnable() {  
    @Override
    public void run() {
        //stuff to run on the main thread
    }
});

This call explicitly sets the thread on which the code should be executed. In my case running my method within such a runnable made Picasso happy, allowed my test to pass, and allowed me to continue using ActivityUnitTestCase.

Hopefully this will help someone. If you have anny questions or comments, feel free to ask :)

Android fragments and memory

This post was initially entitled "Android fragments - the important stuff". It will hopefully provide an overview of the less obvious things associated with utilizing fragments in Android.

What is the difference between attach/add and detach/remove?

Detaching a fragment essentially dissociates a fragment from its activity. The FragmentTransaction documentation states that "the fragment is removed from the UI". This is in my opinion confusing - I understand it to mean that the fragments UI is removed from the on-screen UI. The view is destroyed and is not held in memory. The view is destroyed so as to minimize the applications memory footprint.

The documentation also states "This is the same state as when it is put on the back stack" - that is to say the Fragment instance is stored in memory along with its instance variables etc.

Adding/Attaching a fragment is the opposite.

What am I adding to the backstack?

When you programatically add a fragment into a container, you do so using a FragmentTransaction. A fragment transaction is just a set of instructions for what you want to happen - remove fragment A, add fragment B for example.

When you add a transaction to the backstack, you do exactly that.. you add the transaction. The backstack contains FragmentTransactions not Fragments.

When you pop the backstack you essentially say 'do the opposite of this transaction' or 'do the opposite of the last x transactions'.

As such, when you add a transaction to the backstack using addToBackStack(String name) the name has nothing to do with the fragments involved. It is not a fragment tag or identifier. It is an identifier for the state after the transaction in question.

When you want to reverse the transactions, you use the various popBackstack() methods. More information can be found here.

Memory considerations

As commented in this StackOverflow question, "when a fragment is put on the back stack, its onDestroyView() is called. If at that point you clean up any major allocations being held by the fragment, you shouldn't have memory issues". This quote is vital for appreciating the memory considerations of fragments (and remember.. as mentioned above, this is the same as when you detach a fragment).

When you detatch a fragment, the following lifecycle methods are called: onPause(), onStop(), onDestroyView(), onDestroy(), and onDetach(). If for example you had saved a reference to a visible GoogleMap in an instance variable it would be worth destroying this in onDestroyView() and recreating it if/when you attach the fragment once again and its onCreateView() method is called.

What about show/hide?

FragmentTransaction does have two additional methods - show and hide. I added these to my demo so you can see for yourself that these do exactly what they say on the tin.. they show and hide. No lifecycle methods are called, and every aspect of the fragment instance is held in memory (including the view heirachy).

If you create a lot of fragments, hide them, and add new ones on top (why you would do this I do not know), you will run out of memory sooner or later.

If you have a fragment which has an expensive view heirachy.. and you want it to be off screen for 3 seconds.. these methods may be of use.

Trying it out

The demo application (linked above) allows you to play with these various fragment transactions and view the lifecycle in your logcat output.

What you will notice Is that if you 'Add' a fragment, then 'Remove' it, and then try to 'Attach' the same fragment nothing will happen.

This is because whilst you hold a reference to the fragment, when you remove a fragment the FragmentManager gets rid of its reference to it.

As such when you try and attach it, nothing happens.

Below I have outlined the lifecycle of the various approaches to using fragments. This should outline what happens, and why. With this knowledge you can use the most appropriate methods for your use case.

The lifecycle

1. Add

If you press the add button in the demo, these lifecycle methods are called.

We set an instance variable with the current time to demonstrate how state is saved.

onAttach  
onCreate  
onCreateView  
onActivityCreated  
onStart  
onResume  

Pop

If we had 'Add to backstack' checked, and then pressed 'Pop', these lifecycle methods are called.

onPause  
onStop  
onDestroyView  
onDestroy  
onDetach  

2. Remove

If you have added the fragment, and then remove it.. these lifecycle methods are called.

onPause  
onStop  
onDestroyView  
onDestroy  
onDetach  

Pop

Popping this will re-add the view. As you can see the view is recreated (onCreateView is called) - when removed the view is destroyed competely. Even when the transaction is added to the backstack.

You will however see that the system time outputted is the same. This is because when removed, if added to the backstack the fragment is maintained in a state similar to when it is detatched - its instance variables are retained.

If you were to simply add the fragment, remove it, and then press add again, the system time would be different as under this condition the state is not retained. The full lifecycle (as in 1.) would be executed.

onCreateView  
onActivityCreated  
onStart  
onResume  

3. Detach

If you have pressed 'Add', and then click 'Detach' the following lifecycle methods are executed.

onPause  
onStop  
onDestroyView  

Pop

If at this point you had had addToBackstack checked and you now pop you will see the following lifecycle methods called. You will note that the view is recreated and that the system time is the same.

In fact this is the exact same as a 'post-remove-pop'.

onCreateView  
onActivityCreated  
onStart  
onResume  

4. Attach

If you attach a previously detached fragment, the below lifecycle methods are called.

Again you will notice that the system time is maintained.

onCreateView  
onActivityCreated  
onStart  
onResume  

Pop

If you attach a fragment, add it to the backstack and then pop it, the following lifecycle methods are called.

The difference between this, and a 'post-add-pop is that onDestroy and onDetach are not called.

The significance of this is that after popping you could press 'Attach' once again, and you would notice that the system time is the same.

On the other hand when you 'Add' a fragment, and pop it from the backstack it is completely destroyed - its view.. its instance variables.. everything.

onPause  
onStop  
onDestroyView  

Are you having fun?

Because you are almost certainly having fun.. lets continue.

What the hell is setRetainInstance?!

The Fragment documentation states that it controls "whether a fragment instance is retained across Activity re-creation".

You can play with this in the demo by uncommenting the appropriate line in ExampleFragment.

What it essentially amounts to is that if you add a fragment, and then rotate your screen the following lifecycle methods are called:

onPause  
onStop  
onDestroyView  
onDestroy  
onDetach

onAttach  
onCreate  
onCreateView  
onActivityCreated  
onStart  
onResume  

If you hadn't noticed, this happens to be the same lifecycle as 'removing' a fragment, and then 'adding' it. Guess why? Because that is what is happening :)

What you will notice is that the system time output when it is readded is different to when it was initially added the fragment.

However this is expected.. after all we can see from the lifecycle that the fragment is completely destroyed before it is readded.

Now uncomment the setRetainInstance(true) line in the demo code and do the same thing again.

This time you will see the following lifecycle executed:

onPause  
onStop  
onDestroyView  
onDetach  
onAttach  
onCreateView  
onActivityCreated  
onStart  
onResume  

The important difference is that onDestroy and onCreate are not called. The fragment is not destroyed and recreated but rather its view is destroyed, and state maintained.

You still need to rebuild the view (for all the memory related reasons outlined previously) but the fragment state is retained. The output system time is the same as when it was initially created.

If you have written a beautiful sonnet and stored it in an instance variable (who hasn't..) then you will find that it is stil there when you rotate your screen if setRetainInstance is passed a boolean true.

How does the SDK use this?

The Android SDK uses this stuff everywhere. Fragments are after all a very clever design pattern (if you can call them that).

FragmentTabHost and FragmentStatePagerAdapter are the two which instantly spring to mind.

Given the above you can adapt these classes depending on your needs. For example, if you want a tab host which hosts two fragments that have complex view heirachies yet are regularly changed, you might choose to modify the provided implementation. FragmentTabHost uses attach and detach within its doTabChanged implementation - you could change this to simply show/hide your tabs.

FragmentStatePagerAdapter uses add/remove exclusively, as well as an interesting caching mechanism. This is pretty logical really - if you had a lot of fragments in your pager, you certainly wouldn't want to keep all their views in memory at any given time.

Summary

Fragments seem more complex than they due to confusing documentation and terminology. Furthermore because of the large number of combinations of approaches for different use cases it is pretty difficult to visualise and process what happens and why.

Hopefully the demo app will help you get your head around the concepts, and hopefully my explanations make sense.

If anything needs clarifying or you have any questions.. ask away :)