When you want to manage state in your android application, it can be useful to implement a helper pattern – each activity or class of tasks has a small associated class that keeps track of state you might need to maintain from activity to activity. However, as your app gets quite complex, it can become unwieldy to manage the lifecyle of seventy or eighty helper classes, and you can end up with a lot of them sitting in memory unused, or even worse – sitting in memory unused with references to activities long past – preventing the GC from collecting them!
First things first, let’s check how our app is doing in terms of memory leaks. I find Square’s LeakCanary to be pretty ideal for this job – with a few minor insertions, we’re up and running and can check for leaks.
Looks like a helper is preventing us from garbage collecting our splash activity, with a reference chain from Application -> Helper -> Activity. Someone might need that helper later, but for now it’s just sitting there and preventing us from reclaiming memory. So why not just throw it out, and magically generate a new one next time someone needs it?
Managing it all
Instead of creating a handful of helpers when our application starts up and retaining references to them in the application (bad on two counts – startup time and memory usage) let’s create a tool that can instantiate generic helpers at will, and will allow them to be collected again once their job is done.
First things first, we need to be able to instantiate a generic class – luckily, we have an interface guaranteeing that all helpers can be instantiated one of two ways – with a reference back to the application, or simply as a no-argument constructor. So let’s codify how we want to try instantiated:
So far so good. Now, let’s create a method that will iterate over our potential constructors and attempt to instantiate the helper with each one.
So now we have a way to instantiate any class that conforms to the interfaces we know about. (If you’re wondering what the proxyHelper is about, check out my earlier blog post)
With just this, we have a lazy way of instantiating our managers – instead of generating all of them at boot time, we can now just call tryInstantiation() with the class we so desire and get a fresh helper. But, we still don’t have any way of sharing these helpers around – this is no better than just instantiating them all normally, wherever they are used. This is where one of my favourite Java features comes into play – the weak reference.
Holding on (weakly)
Weak references are a nifty way of keeping track of things when you don’t particularly mind if they still exist later. You can use this to track things, like whether or not an activity or view still exists, but in this case we’ll be using it to monitor what helpers we currently have tucked away in our arsenal. So, let’s create a map of class to reference, and whenever someone asks for a helper check if we don’t first have a (valid) reference to one we made earlier:
Easy enough, right? This way we never duplicate our managers, and if VM pressure gets too high then we can simply let go of any helpers that aren’t strongly referenced by an activity or other end ‘user’.
For convenience (and if you need to do any setup on the manager more than merely instantiate it), you can wrap the getWeakHelper call in a more formal method: