Behind the Code: Actor Model
If you are a true Truecaller fan, you probably know by now that our Android app can be your default SMS client. Bringing in full SMS/MMS support is not a trivial task and our engineering team dedicated months of work providing a zero unknown numbers, spam free inbox experience.
It sounds awesome, but getting there was also quite challenging, mainly because we had to use and sync a lot of threads.
But aren’t threads good? They do enable you to offload work from the main thread ensuring smooth user experience, right?
Absolutely, threads are amazing. We cannot even imagine a non-trivial modern app that does not utilize threads. But there is a catch. Once you start talking about multithreading you start talking about concurrency, deadlocks, synchronization, etc.
At some point in the development, we found ourselves talking more about thread synchronization instead of talking what the threads should actually do. Noticing the impact it had on productivity, we made an agreement – Let’s not spend less time worrying about the dangers of concurrency and spend more time writing the actual code that will complete the task at hand.
So what did you do next?
We started investigating. Concurrency is a well know and heavily researched field. There are a number of mathematical and computational models that solve this problem elegantly. We just needed to find the one that made the most sense for our specific use case. We ended up with the Actor Model.
What is the Actor Model?
The Actor Model is a mathematical model that treats actors as the universal primitives of concurrent computation. It is based on these three simple principles:
- the actor can receive or send a finite number of messages
- the actor can create a finite number of new actors
- the actor can determinate how to respond to the next message receive
All messages passed to an actor are put in a queue and processed sequentially. The actor can only modify its own internal state. All external communication must be done via messages.
This ensures thread safety and eliminates the need for mechanisms for dealing with concurrency (ex. lock).
Did you find an implementation of this model and introduced it to your project?
Well no. Due to the properties discussed, this model is heavily used on the server side (ex. AKKA), but not so much on the client side. All existing Android implementations of this model we managed to find were either too old or too complex. Also, all of them were pure implementation without a support for Android specific components.
Sure, we can bind an actor to a Java thread, but what if we wanted to bind an actor to a Service? Or multiple actors to a ThreadPool? There was nothing out there that offered answers to these questions, so we decided to build a solution from scratch. We ended up with the Android Actor Library, an internal tool that we started using in our project.
Did it help?
It helped tremendously. Android Actor Library is a lightweight tool for binding a worker (an actor) to a thread and making the interaction with this worker as simple as possible. With it, when you are writing the implementation of the worker, you don’t have to worry about concurrency, you can block the thread as long as needed and you can return a result to any thread. It guarantees a sequential, thread safe execution.
Accessing the worker is super easy as well. Just pass a message from any thread and it will be processed on the thread the worker is bound to. Also, you can also bind this worker to Android specific components like Service. Simple, easy, no concurrency, no headaches.
Sounds amazing, are there any downsides to this approach?
The usefulness of this library will largely depend on your need. If you are already using Kotlin or RxJava, concurrency is probably not a huge problem. But, when using Java for Android development, handling concurrency can drastically increase development time.
Switching to a new language or a reactive approach comes with a cost, it takes time until all developers are on the same level of familiarity/expertize and this is where our library comes in handy.
It does have an initial learning curve since you need to type some symbols (annotations, generics, handles), include some auto-generated code that will affect the dex method count slightly and learn how to read the stack traces (that are super informative btw) when you get an exception inside the actor. Also, some of the Android specific features are breaking the actor model. It is heavily inspired by the actor model, but it is not a pure implementation.
And you decided to open source it?
Yes. During these past few months we have been using it as an internal tool, it proved so useful we decided to live up to one of our core Truecaller values – “Help Each Other” and share it with the rest world.
Just check out our GitHub repository to learn more about the library and how easy it is to integrate and use it in your projects.
Also, we would appreciate if you contribute by reporting issues, making pull requests or sending us feedback on how can we improve.