Most contrived Rx examples show you how to replace AsyncTask with Rx. Not many examples show how to abstract away complex async scenarios, such as oneway AIDL Bound Services. Using RxJava with AIDL Services will help us clean up the interface to consume the AIDL service – making it less … gross and easier to work with.
Assume you want to connect to a Bound Service which is
created through AIDL to get some data. Let’s also assume that all the calls on that bound service are oneway calls (async with a callback listener). In order to get the data you need you will have to set up a ServiceConnection then once connected you need to then use the service object to request the data. Since the connected service (AIDL Service) you’re working with only has oneway calls you basically have two layers of indirection you have to deal with before you can get at the data. For example – Suppose we pass in a orderId
and we get an Order object back from the bound service … we have to wait for two async calls to complete in order for that to happen:
- Connecting the Activity to the bound service via the
ServiceConnection
- Making the async call on the service after connection, passing in a callback listener to be called when the data is returned.
Currently anytime I need data I have to connect to a service, wait … query the service, wait … and then get a callback. If I need to make multiples of these types of calls things can get hairy, fast.
The goal here is to wrap this code up somewhere and abstract away the details and simply have an interface that returns Observable<Order> getOrder(long orderId);
Here’s how I’ve gone about implementing this … (full gist).
In the constructor I connect to the bound service with the bindService call. This is async. Once connected in the ServiceConnection I call onNext on the BehaviorSubject that holds the reference to the boundService. Since no one is listening yet, no big deal. That class just hangs outs and waits.
When an interested party subscribes to the getOder call, we return:
PublishSubject<Order>.asObservable()
This way the caller doesn’t know they’re working with a subject (which is recommended).
In that method I subscribe to the orderServiceSubject. If the service connection is still in flight, no big deal – Rx will allow us to wait until onNext on the orderServiceSubject is called. If the service connection has succeeded then the subscriber to orderServiceSubject will get it’s on next called with the bound service as its parameter.
Once we have reached this point, we have a bound service object that we can call methods on. Remember, these methods are set as ‘oneway’, meaning that they are also async and we must supply a callback listener that gets called once the work is complete (at this point it is starting to resemble JavaScript all of a sudden, like whoa).
Inside of this listener (the anonymous Stub class) we use the PublishSubject – orderSubject and call its onNext with the value that was returned. This will propagate the value all the way back to the activity and then we can do whatever we want with it.
As you can see, we can abstract away the nasty AIDL code behind Rx and make it a lot cleaner. Now the client can use RxJava and get the the benefits of Rx without dealing with the ugly guts of the AIDL system.
Special thanks to Dan Lew for proof reading this article.
Here’s the full implementation with an example of how you’d implement this with an Activity.