Common questions on AsyncTask
One of the things I love about my role @ Google, is taking really complex concepts, and breaking them down so that every engineer can understand them clearly. In my recent Android Performance Patterns video, I covered a topic which, on the surface is straightforward, but whose properties can often lead to some unintended side-effects. I am speaking, of course, about AsyncTasks:
This video highlighted some caveats to the AsyncTask that folks may not have been aware of previously. Thankfully we’ve got an awesome android community whose been reaching out with questions:
Let’s dig into a couple of the top questions, and see if we can expand a bit.
Use a WeakReference to the activity / view?
As such, using a WeakReference allows your async work objects to hold references to the particular UI Object you’re interested in, without worrying that it’s going to create a memory leak.
This is fantastic, because it directly addresses the worry we have about memory leaks. But by itself, it’s not a complete solution; It doesn’t solve the next-stage problem : What do you do when the UI Object is destroyed?
If you’re holding a reference to an Activity, the solution could be as easy as just adding some code to check if your WeakReference.get() returns null, in which case the Activity has been destroyed, and you can figure out what to do in that case.
Referencing particular Views has the same safety net, but comes with a little mental anxiety (for me at least). Remember that just because you hold a reference to an object doesn’t ensure that its data state is frozen while the asynchronous work is occurring. For example, the view you’re referencing could have been removed from the view hierarchy, or its contents may have been updated in such a way that invalidates the work that you’ve just done. In either case, you still have to craft a policy on how to deal with those situations.
So, using a WeakReference will help eliminate the potential memory leak, as long as you’re willing to write the extra code per-work-object to properly handle the situation when WeakReference.get() returns null.
What about a static AsyncTask as an inner class?
Correct. Declare the AsyncTask as a static nested class to the activity, and that will remove the issue with the implicit reference. The difference between a “static nested class”, and an “inner class” is nicely summed up in this StackOverflow post:
An instance of InnerClass can exist only within an instance of OuterClass and has direct access to the methods and fields of its enclosing instance
Which helps explain why there’s a leak-issue in the first place. More directly, static nested classes don’t have this same behavior, instances created there don’t require an outer class instantiation.
So this solves your implicit-reference problem, but doesn’t solve the “How do we hold references to UI Objects?” Problem, which is still the primary issue here.
What about RxJava?
Threading is complex, and for a long time now, threading libraries have been successful due to the fact they can abstract out some nuances, and make management of the threading process (from a code perspective) a little easier. I remember when Intel Threaded Building Blocks first came out: I was relieved that there was a great set of battle-tested thread-safe containers that I could use, rather than having to brew those things myself. The result was less time testing threads, and more times planning to enact my evil bidding.
RxJava is yet another threading library, which is designed around a specific type of usage and API model. Due to the functionality provided by the APIs, lots of Android devs swear by it as the backbone of their threading needs. And to be honest, RxJava has some really nice features for those folks who push threading through the roof, and whose apps have a constant flurry of threaded work being passed around.
If RxJava is working for you, then keep rocking that codebase. Don’t change a thing.
For those of you on the fence, I’ll shed a little insight.
Firstly, RxJava is a general purpose java library. There’s nothing specifically Android about it.
For people sharing server & client java code, this could be a blessing, since they won’t have to change mental models about how to pass around work. This does, however, complicate things on the client side, in that there’s extra code / mental cobwebs that have to be maintained about how RxJava fits into the threading model that Android is using.
To be blunt, RxJava is only an API replacement for threading primitives, and does nothing Android-specific to handle the issues of thread safety for UI objects (or other objects, for that matter). You still have to worry about the proper way to reference UI objects, and you still have to write code to handle configuration changes.
Technically speaking, there’s also RxAndroid, which basically forks RxJava to have knowledge of the Application’s MainThread; which makes it easier to send messages to the main thread for execution, but still lacks any knowledge about UI Objects.
This is where framework primitives like AsyncTaskLoader provide a leg up over RxJava/RxAndroid. This class works just like a standard AsyncTask, but provides some extra callbacks to give your work insight into how the activity is changing, so that you can respond in turn. It’s an Android-lifecycle aware threading primitive, which is something that can have an immense amount of value to you, or so says this guy.
At the end of the day, threading primitives are the easy part of the problem; the real issue is what you do with memory. Focus on that.
AsyncTask is just another useful threading primitive. But like anything else, it has caveats that you should be aware of. Knowing the bells-and-whistles helps you to make the best decision possible about what code to write for your app.
In any case, thanks for watching APP, and don’t hesitate to ask questions!