Why Zero-Waste is ushering in the era of the CDP 2.0Read blog series

EngineeringFebruary 09, 2017

Behind the script: Building a Roku SDK

mParticle's Sam Dozor walks us through building our open source Roku SDK and the many eccentricities of the platform that made the experience so unique.

We’re excited to announce that mParticle now supports Roku!

mParticle empowers you to collect data once, and rescue it from a siloed existence by sending it to over 100 partners across the analytics, attribution, marketing, and data-services ecosystem. As with mParticle’s mobile and server-to-server SDKs, the Roku SDK is open source and makes it simple to collect install, session, custom events, and other data within your Roku channels.

Read on to learn more about the SDK and grab the code on Github.

Why Roku?

Businesses need visibility into all of their customer activity – across all platforms. According to comScore, Roku is the most popular OTT streaming device, beating out Chromecast and Apple TV, and has an estimated 49% share of the living room as of 2016. But, for all of its popularity and proliferation, the Roku platform and its 3500+ channels have nearly no official libraries to choose from to support even basic analytics, much less more advanced ecosystem tools.

mParticle’s Roku support lets you send data into the mParticle platform, alongside all of your web and mobile data, and then downstream to other services that otherwise have no integration or API for Roku developers.

Key Features

  • Install and Session Tracking – Automatically collect channel install and upgrade events, along with user-session count and duration statistics.
  • Security – mParticle’s Roku SDK includes support for SSL pinning to keep your data safe while in transit.
  • Device and Application Metrics – Improve your channel’s user-experience by learning which Roku devices are most popular for your user-base, channel upgrade rate, and other crucial metrics such as TV resolution.
  • Device ID and User Attribute Association – Collect the Roku Advertising ID, and associate known user information with your Roku data via mParticle’s user identity and attribute APIs, just as with any of our SDKs.
  • Custom Event Tracking – Study and act on explicit user behavior in your channels with custom events and mParticle’s granular eCommerce API.

Challenges

Roku has some eccentricities compared to the relatively mature iOS and Android development environments, to put it mildly.

BrightScript

First off, there’s BrightScript, the one and only language you can use in a Roku channel. You’d be forgiven if you’ve never heard of it, as Roku is likewise the one and only platform that uses it. It’s a dynamic, interpreted language that looks vaguely like Javascript, and with some fun features like first-class functions. Some example gotchas are that you cannot define classes, making many common object-oriented patterns useless, and all functions you define are global to the entire channel – so you have to be careful not to pollute the global namespace.

Writing idiomatic BrightScript while leveraging well-known design patterns from other languages was uniquely challenging. The ultimate goal was to provide an intuitive interface for channel developers, and a maintainable code base for the mParticle engineering team. It’s crucial for all SDKs or libraries to highlight what developers are meant to use — the public API — and what they shouldn’t be messing with. Similar to Javascript, there’s not much in the way of privacy, or “information hiding,” built into BrightScript. The “module” pattern in Javascript is a common workaround, using closures to define functions privately and expose a different, public interface.

Unfortunately, this doesn’t work in BrightScript:

module = function() as object
    
    privateApi = function()
        print "In a private function!" 
    end function

    return {
        publicApi: function() 
            'Nope! Can't do this - not in scope.
            privateApi() 
        end function
    }
end function

Ultimately, except for truly temporary values, everything in the mParticle SDK is technically public but with a clearly marked private API – an associative array named “_internal.” Some libraries in the Roku ecosystem and elsewhere choose to obfuscate their naming as a way of information hiding, but that can be counterproductive for SDK maintenance, and also developer productivity – we want developers to dive into the source and see how the SDK works, and not have to jump through hoops.

Scene Graph vs. legacy SDK

The Roku platform is in the middle of a transition from the so-called “Legacy” SDK, to the “Scene Graph” SDK. With the older SDK still in wide use, we chose from the onset of development to support both types of channels, which meant some added complexity. Whereas with the legacy SDK there is no multithreading, and limited support for asynchronous operations, Scene Graph provides a solid multithreading mechanism by way of “Tasks.”

Asynchronous Networking

Since a core part of the mParticle SDK’s functionality is the ability to securely and reliably upload data, we needed to nail the nuances of asynchronous networking in both the legacy and Scene Graph contexts. Whereas historically, it’s been a bit of a free-for-all in Roku SDK channels, the latest Roku OS and the channel approval process for Scene Graph impose new restrictions on channels, preventing network, file system, and other slow operations from being performed on the “main” and “render” threads.

Our Roku SDK is designed with popular Roku channel and API patterns in mind. The legacy Roku SDK exposes asynchronous APIs for long-running operations such as network access, despite its lack of multithreading. Channel developers can invoke these APIs and specify a “MessagePort” to receive the results. We wanted all of our APIs to return nearly instantly, so rather than performing a blocking upload whenever developers invoke a public mParticle API, the SDK fires off an asynchronous upload, using a developer-specified MessagePort.

This design turned out really simple to adapt to Scene Graph. With Scene Graph, developers add the predefined mParticle “Task” to each of their channels’ scenes. The mParticle Task has its own run loop, MessagePort, and its own background thread. It’s responsible for initializing the SDK and specifying its own MessagePort – and all of the same message-processing code as in the Legacy SDK is reused.

Cross-thread communication

Having developed a shiny Scene Graph Task, there was a new problem: receiving data via mParticle API calls from other threads. BrightScript does not have any of the typical concurrency support of other languages – there are no mutexes or synchronization mechanisms. You can access a global “Node” within your scene and store information there, but there’s no guarantee of cross-thread consistency. Roku also imposes a strict limitation on the types of objects you can share across threads – notably, function-type objects will be silently stripped as their parent object is cloned from one thread to another.

Luckily, Scene Graph exposes a mechanism by which Tasks can specify a child Node, which can be set to the value of an object from another thread, and the “SGNodeEvent,” allowing a Task to listen for changes to each child Node’s value. This allowed us to create a contract whereby the mParticle Task invokes APIs based on the changing value of a Node. We defined a simple structure – whereby the Node value is expected to contain the method name of a public mParticle API, along with the arguments to pass that API. To abstract this all from channel developers, we created the “mParticleSGBridge” object, which mirrors the public API of the core mParticle SDK object, and manipulates the mParticle Task’s child Node whenever an API is invoked.

Ultimately, these are all just eccentricities of the platform rather than shortcomings, and there’s a refreshing simplicity to the Roku development environment once you overcome the initial learning curve.

Wrapping up

The mParticle Roku SDK is available right now on Github – thanks for reading!

Latest from mParticle

See all insights
Via

Growth

Improve product experience with unified data

Seat Geek

Growth

How SeatGeek tests new channels for growth

Growth

Customer-centric product development