Rx Wrapper How-To for Critical Resource Tasks

Android contains several APIs that hold resources back. They need to be released before closing the current Activity or any other UI part. Doing things the old way, you are responsible for releasing these resources at the end of each activity. With complex tasks, it becomes quite hard to keep track of all of these resources. It is even more complicated when you use them in asynchronous tasks. A small programming overhead will eliminate this problem by replacing the async tasks with Rx streams and scheduling them on the background threads. So, Rx wrappers will ease thread handling. Here is how I use Rx wrappers to make them as awesome as possible:

Rx Wrappers for resource critical tasks for android developers

Basics of wrapping tasks in Observables

 

1. Using Observable.just()

This is the simplest method for wrapping a single Object instance into an Observable. You only need to instantiate the object and the resulting Observable.just(your_object) will be your wrapped object. You need to take into account that the instantiation of the object will run on the caller thread, so if you are using this method on the main thread, it shouldn’t take long to complete.

As you can see in the example, we call the task() function and subscribe to it. The computation part should run on a thread from the Schedulers.io()’s threadpool while we are waiting for the result, our UI thread continues its execution. However the new MyObject() call will run on the UI thread, because the instance is needed to create the Observable on which we are trying to subscribe.

 

2. Using Observable.defer()

The defer method is used when the whole task should run on a background thread. This method should be used for tasks which will only produce a single output and is complete after. Lets see the previous example with a defer() implementation.

As you can see, the Observable.just() call is used inside the Func0 instance’s call() function, which will be called when someone subscribes to the Observable.defer() instance. This call() function will be executed on a thread defined by the Subscription’s subscribeOn call and it returns an Observable which will hold the result of the background task. If you need to use a try-catch block in the call function, you can easily wrap the exception into an Observable by calling Observable.error(Throwable t). It will return to the Action1<Throwable> callback. If the call() function returns, it will automatically call the Observer’s onCompleted callback. If the Observable has been unsubscribed while the task was still processing, the Func0’s call() won’t return to the caller.

 

3. Using Observable.create()

This method is mainly the same as the previous one, but if you use Observable.create(), you must track whether the Observable has been unsubscribed from or not. The basic skeleton should look like this:

 

3.1 Unsubscription callback

Simple Observable implementation looks like the ones shown before, but with the Observable.create() method we can implement a special callback, the unsubscription callback. By defining this, we get a block in which we can release the resources  held and finalize the process: either if the task has been unsubscribed from, encountered an error or if it has been completed. By this we can ensure that no resources will be locked by dead processes. An Observable with the Unsubscription Callback would look like this:

The Unsubscription callback can be considered the final block of the Observable’s inner code. Why is this good? If prepared and programmed correctly, this is an auto-cleanup code for the given task. It can be useful in cases of repeated calls, tasks which hold limited resources and processes running long.

 

Where can we use this in Android?

Repeated http requests are not rare if we use REST. For example, if we get the possible words from the API, wanting to make an autocorrect EditText, there will be tons of http requests when the user types in the first part of the word. If the user is fast, most of the requests should be canceled in the background. This cancel method should be written in the Unsubscription callback. In this case, when we unsubscribe from the previous call, (no matter if it was success or is still in progress,) it will end and only the new http request will be active.

Another example is the Media API, which has long run-times and should be observed during processing, for example you can poll the current progress. The code below shows how to poll the progress and how the media player run-time is wrapped in an Observable stream after it was prepared for start.

The method above works with audio and video and can be modified to work with a TextureView combined with a MediaPlayer (TextureVideoView). We can see that the running code of the base observable only starts the prepared media player and subscribes to the interval observable that will poll the progress of the player. The unsubscription block will take care of the mediaplayer if it was unsubscribed from, crashed or finished playing. As we can see, it will stop it if it is inPlaying state, and then release the held resources.

Another good example is the MediaRecorder API which is pretty similar to the MediaPalyer API. The method is the same, wrap it in a stream and release the resources in the unsubscription block.

 

The special part is the unsubscription part, where we try to stop the recording. If it fails, we delete the output file. If it exists or else, we let the MediaScanner scan the file and finally we release the resources. The main difference between this and the simple mediaplayer is that we need to manage the output file of the released camera here, too.

 

Conclusion

I have shown above that Rx wrappers are great resources to manage your threads and save yourself some time. If you are like me, you will carefully examine your task at hand before you implement any one of these wrappers. Rx wrappers in my opinion serve greatly in making applications (and their development) faster and more efficient.

The editor told me I should encourage you to ask questions in the comments, but I am very likely not to respond. You should try anyway.

Cool things I read when writing this post: