An alternative to RxAndroidPlugins and RxJavaPlugins: Scheduler Injection

In my previous article, I described how the RxAndroidPlugins APIs for RxJava 2 can be used to override Schedulers to create a suitable execution environment for JVM-based unit tests.

As I hinted, that’s not your only option. In this follow-up, I will describe another possibility; Scheduler Injection and we’ll see how it compares to using the plugins approach.

If you are familiar with RxJava 1.x, but haven’t yet started with RxJava 2.x; don’t worry, I’ll mention some specifics from 2.x, but the principles described are still generally applicable to both versions.

Scheduler Injection?

Scheduler Injection is simply supplying the desired instance to your operators, rather than letting them run on their defaults.

It is most succinctly performed by employing the Strategy Pattern. You start by declaring an interface; which defines access to all the Schedulers that your app uses.

In your production code, you then implement this interface so that it returns the corresponding Scheduler instances from RxAndroid, RxJava or your own custom Scheduler. It would look something like this:

Now wherever a Scheduler is used, you supply that class with the instance (via constructor or dependency injection framework) and reference the corresponding Scheduler in your operators. For example, say we originally have the sequence:

This becomes -

Importantly, all explicit and implicit use of Schedulers must be replaced by their equivalent from the . Naturally this includes those referenced in operators such as and , but also in operators like and which operate on a particular Scheduler unless otherwise specified.

@SchedulerSupport

In RxJava 2, you can tell if an operator implicitly uses a particular Scheduler by inspecting the annotation on its declaration. This annotation has no inherent run-time effect, but rather it serves as documentation of the Scheduler used by the operator.

If we look at the operator in RxJava 2’s class we can see that its annotation has the value , meaning that it uses internally -

Image for post
Image for post
RxJava 2.x delay operator using Schedulers.computation()

These types of operators will also have an overloaded equivalent, annotated with the value , which allow you to manually specify the Scheduler to be used -

Image for post
Image for post
RxJava 2.x delay operator with Scheduler parameter

You just need to change your operator method invocation to the overloaded variant and pass in the appropriate Scheduler. Pretty simple!

Operators that don’t use a Scheduler, such as , have the annotation value . Their invocations won’t require any changes.

In Your Tests

Now as we know from my previous article, we can’t use the implementation in our JVM-based unit tests due to the Android dependencies and asynchronous scheduling; we need a different implementation.

The exact implementation depends on the particular requirements of your test. That’s where the Scheduler Injection pattern shines; you can override the Schedulers in whatever way you like and modify them whenever it suits you.

In our example, we want to verify the influence of the operator, so we would use a backed by an instance of RxJava’s .

This is quite a straightforward task; it’s just another implementation of injected into the class under test. See the example project for the details.

Verbose but Flexible

When compared to using the Plugins, you can see that the additional constructor dependency and operator parameter could add a bit of unwanted verbosity and wiring ceremony to your code.

Another potential concern is that you might occasionally forget to supply the additional parameter to the operator, leading to unpredictable test results and wasted time when debugging.

On the upside, using the gives you complete flexibility and control over your Schedulers. You won’t need to be concerned with the quirks of the Plugin APIs, nor will you need to get your head around the Plugin API changes from RxJava 1.x. Furthermore, if you are using your own custom Scheduler, then it’s simple to add that into your and override it when necessary.

All this means that with the , you have a single place where you can freely manage your Schedulers.

Decision Time

So what approach should be used? Should you use the Plugins or use Scheduler Injection?

My recommendation for app developers is to use Scheduler Injection.

I’ve used it in large production apps and in my experience the flexibility that you gain outweighs the issue of verbosity. With a little bit of repetition and awareness, using it becomes second nature. I haven’t found myself forgetting to supply a Scheduler; any reasonably comprehensive set of tests would make that painfully obvious to you.

The Plugins might be less intrusive to your production code, but when it comes time to test, their limited flexibility and somewhat quirky initialization sequence eliminates those gains.

So when would you want to use RxJavaPlugins or RxAndroidPlugins?

Well, they remain helpful for library developers who might not want to expose Scheduler configuration to clients, but want control when testing. Also, they could be an option if you’ve inherited a legacy code-base which somehow makes extensive use of RxJava, but not of dependency injection. Hopefully that’s not your situation!

So, if you can, go down the Scheduler Injection path and write your own .

Thanks for reading!

See the full example source code here.

Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store