There’s Life After AsyncTasks in Android
Indeed, I’m one who thinks that AsyncTasks are one of the most helpful and easy tools for getting some work done in the background without concerning too much about sensitive lines of code.
What I pretend to cover here is a smart way of dealing with a bit more complex situations. Although it represents an ideal way of building a RESTful API, as you may see, it is applicable to a wide range of scenarios with minor tweaks.
Let me take the problem a pair of levels of abstraction up. Think about your requests as hungry dogs 3 seconds after smelling one of the best pieces of meat that one can ever find. The meat is your backend endpoint. The result is that you put the dogs there but have no idea about how in the world are they going to get to the meat, and not only in which order but in which state. They can even fight between each other, they are very hungry.
AsyncTasks use to be like this before 3.0, and although it is different now (3.0 onwards) it feels some sort of the same.
But we live in a world full of animals, why not using ants? They gather food in a ordered and prioritized way and they stick to the rules until they reach their destination. That definitely sounds like a better plan.
Note from now that with the approach proposed we’ll be solving some other major issues down the road (screen orientation changes, idle states, inactive activities, data loss, user experience, etc).
For a clearer understanding I’ll cover the general architecture of the proposed system first in this post while digging more in depth in each of the pieces of it in upcoming ones.
Let’s get back to our ants. Remember, ants represent our requests, the route they use to get to the food and back will be our queues and pool executors and the number of lanes in this route will be the number of threads that we’ll be allowed to use concurrently. In case of emergency when a critical request should need to be executed we’ll create a new concurrent thread.
Since ants are foreseeing a complex winter they had to build new storing rooms which because of the rush are not perfect. For this rooms they’ll need to prioritize food attending to its size to make it fit properly, big pieces first and small after, these are our independent requests. For this kind of tasks they have designated some high trained ants so that the team looks like this:
- Secret service ants: This ants will play a special role. They will be responsible for selecting the pieces of food regarding to size and bringing it back in rigorous order of priority. These are our independent requests (normally GET) with a high priority whenever we need to present instant information to the user.
- General ants: This ants will be taking care of the rest of the food. This ants depend one on the other to find the right way back home safely. They represent our dependent requests (normally linked PUT, POST and DELETE – i.e: create a new user account -> assign a new picture for this user).
I believe there is now a clear big picture of the approach, let’s summarize and complete the analogy:
- Ants are requests.
- The route they follow are our queues and pool executors.
- Number of lanes for secret service ants is the number of threads we’ll want to use for independent requests.
- (+) The whole scenario will be a service (we’ll want the ants to bring home some food regardless of the time of the day).
- (+) The notification of new food to the king of ants will be solved by a callback, but not an usual one. Since the king is a busy ant, he only wants to be notified about new food just in cases when he is not busy performing any other task. Nonetheless, he doesn’t want to lose new food (it’ll be a difficult winter) so the rules are for the ants to store the food first and to try to notify him after. (That’ll protect us against situations when the user is in any other different activity, in any other app or not even using the device). Aren’t these ants smart?
Wow, seems that we have a super-consistent system that will allow us to avoid paining in winter, it will be gorgeous.
- Our requests will be performed by Callables (ants with a certain designated task).
- We’ll set a default number of 3 concurrent threads for prioritized and independent requests (this number will only change if any critical request needs to be executed and the pool of concurrent threads is already full).
- That will lead to two different queues and executors for our route (unbounded priority queue + 3-thread pool executor / unbounded serial queue + single-thread pool executor).
- For this case a bound service will be used. We need a way to be in touch with our ants and ideally to be able to use them for more than one type of operation.
- Notifications about the new information will be performed through callbacks dispatched by the callables. This callbacks will be implemented within the same activity that requested the task. This way we’ll avoid performing unnecessary operations. Prior to dispatch the completion of the task the important information will be stored in memory (sqlite, sharedprefs, etc). Note this meets exactly the requirements described: our ants will store the food before notifying the king, and the notification will only happens if the latter is available.
That sets the basis of our structure. Again, this can be applied to several systems which eventually will be simpler than the case of a RESTful API (file processing, game events/animations, etc). In the upcoming posts, every element will be described in detail.
Ultimately I encourage you to share your thoughts around the topic. That will definitely enrich and improve even more the “Ant’s approach”.