Abstracting Analytics: RxJava Observers in TV Apps
Sometimes analytics are nice to have. Sometimes they're critical, like in our Fire TV apps we just published on Amazon.
Content providers and distributors are always negotiating who can show what content (and for how much). However, I imagine it's much harder to turn down the content that we, the viewers, find most valuable. Analytics are one way to find out what shows we're really watching; without analytics, it's possible that some of our favorites, like Firefly, could be cancelled after only one season!
Analytics is a broad term for many forms of data collection. In our TV apps, for instance, we might use analytics for:
- Bug/Crash Logging
- Screen View Tracking
- Audience Measurement
- Quality of Experience Measurement
Here's how we approach such a broad set of requirements.
Abstractly speaking, we'll consider two goals:
- We want to handle screen views and events asynchronously with multiple subscribers.
- We want a simple, central location to which we can publish our screen views and events.
In Java, we define a simple interface to meet our two goals.
An RxJava Observable
ReactiveX, or Rx for short, is a library built specifically for composing and consuming asynchronous data. Rx includes an
Observable, which emits a sequence of items. We can create
Observers which subscribe to those sequences. In our case, we want an
Observable which emits screen views as they occur and
Observers which react to those screen views.
For our implementation, we like the RxJava
PublishSubject, which makes it easy to emit screen views and subscribe to them via a single object.
addObserver lets us create a new subscription to our
sendScreenView lets us emit a new value to our
While it's convenient to have
subscribe available in the same object, we also want to be diligent about the number of subscribers we have running in our app, so we're careful to instantiate our
AnalyticsService as a singleton. Be sure to check your particular ReactiveX implementation or conventions when choosing your Rx objects.
Our First Observer
Perhaps we'll start with a really simple screen view handler: printing the screen name.
IAnalyticsService interface expects
Observer<String> objects, so we'll implement one in a
Although we might have
onComplete implementations in our final product, for the sake of this post we'll just add a
onNext and leave empty implementations for the rest.
The Google Analytics Observer
println was a little too simple, so we should hook up something more meaningful, like Google Analytics.
It's actually not much more difficult, however, because a Fire TV app is really just an Android app, and Google has a package for Android. To get started, we added that package to our
GoogleAnalyticsObserver implementation of
It's not too different from the
- We don't really want to block the rest of our app when we're sending our screen views, so we use an RxJava
Completableto defer all of that HTTP traffic.
- We pass a reference to our Google Analytics
Trackerin the constructor for our
Hooking It All Up
Now that we have two different
Observers for our screen views, we just need to instantiate them and pass them to our
AnalyticsService implementation. Considering we'll have a few more of these
Observers, we'll need to refactor later on, but for now we'll just add calls to
addObserver at the end of our app's
Because we send a lot of analytics events throughout our app, we also created the
sendScreenView static method. Any time we need to send a screen view, it's a simple call to
FireTvApp.sendScreenView(currentShowName); from the views themselves or (in our case) the navigation between them.
Is this working?
Taking a look at our
logcat output, we find the output from our
I/System.out( 6555): Analytics Screen View: Firefly
With Google Analytics debug logging enabled (
adb shell setprop log.tag.GAv4 DEBUG) we also see our Google Analytics screen view:
D/GAv4 ( 6555): Hit delivery requested: aid=com.infernored.firetv, an=IRT TV, cd=Firefly, t=screenview, …
Or if you're not in a command-line mood, you could always check your real-time analytics.
Just like that, we're set up with screen view tracking for logging and Google Analytics, and it's on to all the other