Counting threads — An Rx guide

Maxime Roussy
Life360 Engineering
4 min readMar 19, 2021

In early 2020, several reports came in about an odd bug where users would launch the app and be presented with a blank or empty UI.

Blank/empty UI

Most users would report seeing the issue only once, and not seeing it on future launches, without changing any behaviour. Nobody was able narrow down repro steps to help with troubleshooting.

After weeks of digging into our code and trying to understand how this type of issue could even happen, we finally got a clue. While simulating slow or spotty network connectivity (this is easily done using the Android Studio Emulator and tweaking the cellular settings)

Android Studio Emulator Cellular Settings

we discovered that the app would occasionally take much longer to load the initial UI. I mean it was painfully slow, upwards of 10 seconds. So it seems the issue wasn’t that things would never load, it’s that it took so long it seemed like it never would (who’s going to stick around looking at a blank screen for that long, right?).

But wait, all this data is cached on the device, how could slow network connectivity cause the screen to load this slowly? Shouldn’t the local data be served on launch while tasks run in the background to sync and update from our remote source?

RxJava Schedulers

Let’s take a quick detour to look at RxJava schedulers, this will make more sense in the next section.

Schedulers.computation()

The computation Scheduler is backed by a CPU-bound thread pool that is mainly recommended for CPU intensive workloads, such as large data set processing and intensive loops and callbacks.

Schedulers.io()

The IO Scheduler is backed by an unbound cached thread pool that creates new threads as requested, but it also caches and re-uses threads and will scale the amount of threads needed. It’s recommended for light workloads such as local and remote data calls and file operations.

*There are also other schedulers available to use in RxJava with different behaviors (single, trampoline, newThread, and mainThread, see https://medium.com/android-news/rxjava-schedulers-what-when-and-how-to-use-it-6cfc27293add for more details)

Digging deeper

Now, this blank UI bug took me down deep into our codebase, profiling our data from device storage all the way to the UI, breaking down our syncing process with remote sources, and tracking down the different dependencies on our dagger graph.

After this extensive investigation, I noticed something that seemed odd. I was looking at other dependencies in a module, when I noticed that our background work scheduler provided for all dependents was the computation scheduler.

What’s the big deal?

So, what’s the big deal?

Well, using the computation scheduler was fine, most of the time, but during network slow downs or with a spotty connection what would happen is threads would take much longer to finish their work and release, starving other tasks and streams in the process. That’s why, things would eventually load as the threads completed or timed out and became available, even though it was after an unbearable amount of time.

During our app launch, there’s an incredible amount of calls and work happening as we initialize all our screens, load data from local storage, start syncing with our remote source and start background location updates.

Photo by shun idota on Unsplash

We opted to change most of our Schedulers for background work to the io scheduler and we selectively kept the computation scheduler where we thought it was important. Typically those cases were where we do longer intensive tasks that we want dedicated resources for.

All is well again

After the change, the issue was resolved, and the app even starts up a little faster now.

The journey to this change actually prompted us to also have proper loading and error states on the main screens when there is no data or when loading the data takes too long. A loading spinner goes a long way, compared to staring at a blank screen!

Come join us

Life360 is the first-ever family safety membership, offering protection on the go, on the road, and online. We are on a mission to redefine how safety is delivered to families worldwide — and there is so much more to do! We’re looking for talented people to join the team: check out our jobs page.

--

--