Analytics Android Collector

This guide describes how to set up the Android Collector v3. You can find a guide for the deprecated v2 collector here.

How to set up Analytics on Android

Bitmovin Analytics enables you to get useful insights into the video experience in your apps. The Bitmovin Player for Android includes Analytics pre-integrated. Players like ExoPlayer, Media3 ExoPlayer, and the Amazon IVS player can be tracked through our player-specific collectors.

In order to complete this guide, you need at least a basic setup of your chosen player. If you haven't set up your Player yet, go to the corresponding Player documentation page first: Bitmovin Player getting started guide, ExoPlayer documentation, Media3 ExoPlayer documentation, Amazon IVS Player documentation.

You also need to set up your analytics license and allowlist your domain. If you haven't done this yet, go to our How to Set Up page and follow the steps there.

Platform requirements:

CollectorSupported Android Version
Bitmovin Player CollectorAndroid 5+
ExoPlayer CollectorAndroid 5+
Media3 ExoPlayer CollectorAndroid 5+
Amazon IVS Player CollectorAndroid 5+

Bitmovin Player

Step 1: Configure Analytics

Starting with version 3.41.0 of the player, Analytics comes pre-integrated in the player. You just need to enable Analytics during creation of the Player.

Enable Analytics for the Player

For a minimal setup, only the license key needs to be specified. For more analytics configuration see here.
This minimal setup is enough to enable basic tracking.

val playerConfig = PlayerConfig()
val analyticsConfig = AnalyticsConfig(licenseKey = "<ANALYTICS_LICENSE_KEY>")
val player = Player(context, playerConfig, AnalyticsPlayerConfig.Enabled(analyticsConfig))

Enrich analytics data with Metadata

In order to enrich analytics with more data, DefaultMetadata and SourceMetadata can be set.

DefaultMetadata can be set during player creation, and this contains source independent data.

val defaultMetadata = DefaultMetadata(
  	customUserId = "userId",
  	customData = CustomData(customData1 = "appVersion4")
  )

val player = Player(
  context,
  playerConfig,
  AnalyticsPlayerConfig.Enabled(analyticsConfig, defaultMetadata),
)

SourceMetadata can be set for each source to add more source-specific metadata.

val sourceMetadata = SourceMetadata(
  	title = "stream title",
  	videoId = "exampleId"	,
  	customData = CustomData(customData2 = "ExampleGenre")
  )

val source = Source(sourceConfig, AnalyticsSourceConfig.Enabled(sourceMetadata))
player.load(source)

Step 2: Check Statistics in Dashboard

After the setup is done there's nothing more to do. Events are recorded automatically and you can head over to the Analytics Dashboard to see your metrics.

3rd Party Players

Step 1: Add the SDK to Your Project.

Add the Bitmovin release repository to your project

Add a link to our release repository to your application's build.gradle file. In addition to that, the Google maven repository must be added.

allprojects {
    repositories {
        mavenCentral()
        google()

        maven {
            url 'https://artifacts.bitmovin.com/artifactory/public-releases'
        }
    }
}

Add the Dependency to the Project

Add the Bitmovin Analytics Android SDK as a dependency to your main project's build.gradle file as shown below, while replacing {Version Number} with the desired SDK version number. The available SDK versions are listed in our Release Notes. We recommend to always stay up to date with both player and collector versions. However if you are using an older player version, check out which collector version you should use at our Android Collector Github page.

dependencies {
    implementation 'com.bitmovin.analytics:collector-media3-exoplayer:{Version Number}'
}
dependencies {
    implementation 'com.bitmovin.analytics:collector-exoplayer:{Version Number}'
}
dependencies {
    implementation 'com.bitmovin.analytics:collector-amazon-ivs:{Version Number}'
}

Step 2: Setup the Collector

First create a basic AnalyticsConfig object with your Analytics License key. You can further customise it by adding optional configuration parameters. For more information and a complete list of configuration fields see our Configuration Guide.

Please replace ANALYTICS_LICENSE_KEY with a license key from your account. Please checkout How to set up to learn more about how to get to your analytics license.

Create the collector object by passing the config object. Finally attach your player to the collector. Always detach your player from the collector once you are done, e.g. when calling the player.release() method.

// Create a AnalyticsConfig using your Bitmovin analytics license key
val analyticsConfig = AnalyticsConfig("<ANALYTICS_LICENSE_KEY>")

// Create Analytics Collector for Media3 ExoPlayer
val analyticsCollector = IMedia3ExoPlayerCollector.Factory.create(applicationContext, analyticsConfig)

// create and set SourceMetadata
val sourceMetadata = SourceMetadata(title = "exampletitle", videoId = "exampleId")
analyticsCollector.sourceMetadata = sourceMetadata

// Attach your Media3 ExoPlayer instance
analyticsCollector.attachPlayer(player)

// set mediaItem and start playing video after attaching
player.setMediaItem(mediaItem)
player.prepare()
player.play()

// Detach your player when you are done. 
// For example, call this method before releasing the player
analyticsCollector.detachPlayer()
// Create a AnalyticsConfig using your Bitmovin analytics license key
val analyticsConfig = AnalyticsConfig("<ANALYTICS_LICENSE_KEY>")

// Create Analytics Collector for ExoPlayer
val analyticsCollector = IExoPlayerCollector.Factory.create(applicationContext, analyticsConfig)

// create and set SourceMetadata
val sourceMetadata = SourceMetadata(title="exampletitle", videoId="exampleId")
analyticsCollector.sourceMetadata = sourceMetadata

// Attach your ExoPlayer instance
analyticsCollector.attachPlayer(player)

// set mediaItem and start playing video after attaching
player.setMediaItem(mediaItem)
player.prepare()
player.play()

// Detach your player when you are done. 
// For example, call this method before releasing the player
analyticsCollector.detachPlayer()
// Create an AnalyticsConfig using your Bitmovin analytics license key
val analyticsConfig = AnalyticsConfig("<ANALYTICS_LICENSE_KEY>")

// Create Analytics Collector for Amazon IVS Player
val analyticsCollector = IAmazonIvsPlayerCollector.Factory.create(applicationContext, analyticsConfig)

// create and set SourceMetadata
val sourceMetadata = SourceMetadata(title="exampletitle", videoId="exampleId")
analyticsCollector.sourceMetadata = sourceMetadata

// Attach your Amazon IVS Player instance
analyticsCollector.attachPlayer(player)

// load and start playing video after attaching
player.load(sourceUri)
player.play()

// Detach your player when you are done.
// For example, call this method before releasing the player
analyticsCollector.detachPlayer()

When switching to a new video we recommend that you follow the sequence of events below.

// Detach the player when the first video is completed
analyticsCollector.detachPlayer()

// change SourceMetadata
val sourceMetadata2 = SourceMetadata(title = "exampletitle2", videoId = "exampleId2")
analyticsCollector.sourceMetadata = sourceMetadata2

// Reattach your player instance
analyticsCollector.attachPlayer(player)

player.setMediaItem(mediaItem2)
player.prepare()
player.play()
// Detach the player when the first video is completed
analyticsCollector.detachPlayer()

// change SourceMetadata
val sourceMetadata2 = SourceMetadata(title="exampletitle2", videoId="exampleId2")
analyticsCollector.sourceMetadata = sourceMetadata2

// Reattach your player instance
analyticsCollector.attachPlayer(player)

player.load(source2Uri)
player.play()

Step 3: Check Statistics in Dashboard

After the setup is done there is nothing more to do. Events are recorded automatically and you can head over to the Analytics Dashboard to see statistics.

Threading Model

The collector API is not thread safe. All calls need to come from the same thread as the player is executed on (usually the MainThread). While the collector might not crash when called from different threads, it can lead to inconsistent data.

Minification / Obfuscation

The collector uses the @Keep annotation to prevent minification/obfuscation of request and response classes. There could be issues with certain obfuscation/minification tools though (seen with DexGuard for example), and the following rules should be applied in these cases:

#### Bitmovin Analytics
-keep class com.bitmovin.analytics.data.** { *; }
-keep class com.bitmovin.analytics.features.** { *; }
-keep class com.bitmovin.analytics.license.** { *; }

Common Integration Pitfalls

Media3-Exoplayer

AbstractMethodError in media3 AnalyticsListener

Depending on the Release Configuration, Default Methods of media3 AnalyticsListener might be stripped away, which leads to an AbstractMethodError with a StackTrace similar to below:

java.lang.AbstractMethodError: abstract method "void androidx.media3.exoplayer.analytics.AnalyticsListener.onPlayerStateChanged(androidx.media3.exoplayer.analytics.AnalyticsListener$EventTime, boolean, int)"
at androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector.lambda$onPlayerStateChanged$36(DefaultAnalyticsCollector.java:549)
at androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector$$ExternalSyntheticLambda17.invoke(D8$$SyntheticClass:0)
at androidx.media3.common.util.ListenerSet$ListenerHolder.invoke(ListenerSet.java:339)

To fix the issue the following gradle setting needs to be applied in the gradle.properties file:

android.useFullClasspathForDexingTransform = true

This issue arises due to a bug with desugaring and default Methods of Kotlin/Java.
Reference: https://issuetracker.google.com/issues/230454566

Examples

You can find fully functional code examples in our Github Repository.