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
- Advertising
Here's how we approach such a broad set of requirements.
Our Goals
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 PublishSubject
. sendScreenView
lets us emit a new value to our PublishSubject
.
While it's convenient to have onNext
and 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.
Our IAnalyticsService
interface expects Observer<String>
objects, so we'll implement one in a LoggingAnalyticsObserver
.
Although we might have onSubscribe
, onError
, and onComplete
implementations in our final product, for the sake of this post we'll just add a println
into onNext
and leave empty implementations for the rest.
The Google Analytics Observer
Perhaps the 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 build.gradle
file:
compile 'com.google.android.gms:play-services-analytics:10.2.1'
Here's a GoogleAnalyticsObserver
implementation of Observer<String>
.
It's not too different from the LoggingAnalyticsObserver
, except:
- We don't really want to block the rest of our app when we're sending our screen views, so we use an RxJava
Completable
to defer all of that HTTP traffic. - We pass a reference to our Google Analytics
Tracker
in the constructor for ourObserver
instance.
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 onCreate
.
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 LoggingAnalyticsObserver
:
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 Observers
!