Crunching RxAndroid — Part 3

Learning Reactive Programming the easy way

Roberto Orgiu
Crunching RxAndroid

--

In the previous article, we introduced ourselves to the lambdas, anonymous functions that can make our whole code look much cleaner, simpler and more beautiful… but how can we use this bunch of knowledge in a real life project?

The starting point

Lately, I released a new version of WhatSong, an app that once started as DashClock extension and it’s now a hub for creating shortcuts for the most famous music recognition providers (such as Shazam) available on Android.

If you wish to try it, just head over the Play Store link and download it, it’s free!

Download WhatSong from the Google Play store

During the development of this app, I had the need of knowing which of the supported providers were installed on the device, so that the users could only choose between a few valid alternatives, or use the Google Now recognition service (since the app is only available within the Play Store, it is a fair assumption to believe that Google Now is always installed).

The algorithm

Let’s tackle down the sequence of steps we have to take in order to understand how to refactor the code:

  • take the list of supported providers
  • for each of the providers, check if they are installed
  • if they are not, remove it from the list
  • return the list of installed (and supported) providers

Since this task could be called any number of times and the list of installed providers could change, I decided, back in the days, to use an IntentService to provide the requested information. You can see the full source code of this class on GitHub or on PasteBin.

Rx-ify all the things!

Now, we know the steps we have to take, and we have more than 100 lines of IntentService to play with… let’s see if we can make it Reactive!

First, we have to change a little bit the way we are thinking the algorithm: we begin with emitting one after the other all the packages of the music recognition softwares we support:

Observable.from(softwarePackages)

Then, we create another Observable with the software names, so that we can combine them to create a PackageData object:

softwareNamesObservable = Observable.from(softwareNames);

Now that we have the two Observables emitting the list of packages and names, we can combine them, using the ZipWith operator, that takes the second Observable and combines it with the first one using the function provided as second parameter. In this case, our function will just create a new PackageData and we could rewrite this behaviour as a method reference pretty easily:

Observable.from(softwarePackages)
.zipWith(softwareNamesObservable, PackageData::new)

At this point, we have an Observable emitting PackageDatas, but we are not filtering the results in any way. To do so, we have to first find the operator we need: in this case, I used the filter one, that will take as argument a function and will return a boolean: in case this return value it’s true, the chain keeps on going, otherwise it will be interrupted.

Let’s define a condition that will return true if the given PackageData is installed on the device:

private static boolean isAppInstalled
(final Context context, String packageName) {
boolean isAppInstalled = true;
try {
context.getPackageManager().getPackageInfo
(packageName, PackageManager.GET_ACTIVITIES);
} catch (Exception e) {
isAppInstalled = false;
}
return isAppInstalled;
}

Now that we have our function, we can just apply it to our Observable, so that we only get the installed packages:

Observable.from(softwarePackages)
.zipWith(softwareNamesObservable, PackageData::new)
.filter(packageData ->
isAppInstalled(context, packageData.getPackageName()))

The rest of this part is just executing this chain on the I/O Scheduler and return it on the UI Thread:

Observable.from(softwarePackages)
.zipWith(softwareNamesObservable, PackageData::new)
.filter(packageData ->
isAppInstalled(context, packageData.getPackageName()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());

We are now able to successfully filter the list of supported and installed providers on our device on a background thread and have it delivered on the main thread… we just need to use it! What we need now it’s a simple Subscriber, that will take the result of the Observable and will use it in some way: in the WhatSong app, we decided to just add each and everyone of the installed apps to an internal list (when the onNext event was triggered), that would be then converted into an array when the Observable completed its work.

.subscribe(Subscribers.create(
(installedApps::add),
(thowable -> { /* on error */ }),
(this::convertInstalledAppsListToVector) ));

Conclusion

We just saw how we could refactor an existing project and make use of the Reactive Programming in our every day routine, but this is not the end of the journey, we can apply this ideas in many different ways!

If you feel like studying a bit more the code we just explained, head over to the GitHub repo, with a deeper focus to the ShortcutPresenter and InstalledAppsUtils classes, where this code is effectively used.

--

--