Setup (Android)

Get started using Reaper in Android in under 10 minutes

šŸ“˜

Reaper for Android is currently in Beta

Reach out to [email protected] or fill out this form for access

Reaper is an SDK that uses production data to detect dead code. See the emerge-android repo for source code.

Quick setup (~10 minutes)

Install the SDK

First, if you haven't already, you must install our Gradle plugin by following our setup guide. The Gradle plugin is used to instrument your application code.

Second, add an implementation dependency for the Reaper SDK:

Latest versions:

  • Gradle plugin: Gradle Plugin Portal Version
  • Reaper SDK Maven Central
// app-module build.gradle.kts
dependencies {  
  implementation("com.emergetools.reaper:reaper:<latest_version>")
}

Configure the SDK

The SDK automatically sends user sessions to Emerge's backend, allowing Emerge to determine used & unused code.

To send sessions we need to configure two settings within the reaper block: enabledVariants and publishableApiKey. Additionally, Emerge needs the apiToken to be set for uploading the AAB with Reaper to Emerge.

  • apiToken is a token used by the Emerge Gradle plugin for uploading the outputted AAB with Reaper to Emerge. This uploaded AAB is used by Emerge to find all possible classes in the app. You can create a new API token here.
  • enabledVariants is a List property that controls whether the plugin instruments the bytecode during compilation. for the specified variants
  • publishableApiKey is a token included with Reaper reports uploaded from the field to Emerge. The key can be found here.
// app-module build.gradle.kts
plugins {
  id("com.emergetools.android")
}

emerge {
  // Emerge uses the EMERGE_API_TOKEN env variable by default if you don't set a value explicitly.
  apiToken.set(System.getenv("EMERGE_API_TOKEN"))
  
  // ...
  
  reaper {
    enabledVariants.set("release")
    publishableApiKey.set("<PUBLISHABLE API KEY>")
  }
}

Setup verification

Emerge offers a task, ./gradlew :<app>:emergeReaperPreflight<variant> to verify Reaper is set up properly for each variant of your application.

ā€ŗ ./gradlew :app:emergeReaperPreflightRelease

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

> Task :app:emergeReaperPreflightRelease

ā•”ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•—
ā•‘ Reaper preflight was successful (3/3) ā•‘
ā• ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
ā• ā• āœ… Reaper enabled for variant: release
ā• ā• āœ… reaper.publishableApiKey set
ā•šā• āœ… Reaper runtime SDK added

Any errors in the Reaper configuration will be raised here with instructions for how to address them.

Build and upload the release AAB

šŸš§

Reaper is intended to run on Release builds

Emerge recommends only enabling Reaper on non-debug variants.

Even if just for testing, Emerge does not recommend leveraging debug builds with reaper, as it can lead to inaccurate results due to lack of minification and other optimizations that occur only in release builds.

Emerge requires a release AAB with Reaper to be uploaded, as Emerge analyzes the app to determine all classes available.

This is done automatically by the default AGP ./gradlew :<app>:bundle<variant> Gradle task. Emerge hooks the bundle tasks for any variants specified with enabledVariants and uploads the AAB automatically to Emerge.

Emerge will then parse the uploaded AAB and find all possible classes in the app, which will later be marked as used/unused when user reports are uploaded.

Viewing Reaper data

All versions of your app with Reaper included can be viewed in the uploads page, under the "Reaper" tab: https://www.emergetools.com/uploads?tab=reaper.

The Reaper versions tab contains all app versions with valid Reaper data, viewable from the "Uploads" page.

The Reaper versions tab contains all app versions with valid Reaper data, viewable from the "Uploads" page.

Clicking on a version will take you to a detailed breakdown of class usage information for that app version.

Reaper class usage information can be viewed in the Reaper tab.

Reaper class usage information can be viewed in the Reaper sidebar tab.

Once Reaper is in your app, as your users use the application, reports will be sent and analyzed by Emerge to aggregate class usage information.

Note: After setting up/uploading for the first time, it's expected for an app to have all classes marked as unused and no session counts. Once you release your app with Reaper integrated and sessions begin to be uploaded, Emerge will reflect unused classes and session counts in the UI.

Publish the built AAB

The ./gradlew :<app>:bundle<variant> will generate the AAB for the specific variant specified in the standard output directory (<app>/build/outputs/bundle/<variant>/app-<variant>.aab) with Reaper set up.

Emerge recommends publishing the built AAB to Google Play and any other publications as the final release build artifact.

šŸ‘

That's it!

Once published, the Emerge SDK. will begin uploading real code usage reports to Emerge.

Read on for more information on how Reaper works and how to interpret results.

How does it work?

Reaper works by instrumenting your app's code using Emerge's gradle plugin. The plugin adds "breadcrumb" logging, which records hashed information about code as it's used.

The Reaper SDK will aggregate this information into a background report. Once users leave your app, the Reaper SDK will upload the report to Emerge. Emerge will then connect the hashed class usage information on our backend with the detected classes in your app, which is then used to determine which code is used and unused.

Emerge will then reflect which code is unused in the Reaper UI. Emerge recommends waiting some time for a critical amount of sessions to be received before taking action on deleting unused code.

Performance impact

Emerge has heavily tested Reaper on apps like Hacker News and Now in Android. The overall performance impact of adding/using reaper is negligible. Reaper adds ~2 instructions to the init method of a class.

Nonetheless, Emerge would encourage adding Reaper into a beta/internal release track before rolling out to production and validating using Emerge performance testing or any other performance metrics your team currently leverages.

Size impact

The overall size impact of Reaper is a function of the number of classes in the app. The SDK itself adds ~12kb of download size to an app. Additionally, Reaper relies on class instrumentation, which will add a few instructions and an 8-byte hash string to each class.

For apps like apps like Hacker News and Now in Android, we've seen a ~5% increase in download size from adding Reaper.

SDK initialization

Automatic initialization

Reaper relies on the Androidx startup library to auto-initialize the Reaper SDK as part of your app's startup.

Disable automatic initialization

To disable automatic initialization add the following snippet to your app's AndroidManifest.xml:

<provider
  android:name="androidx.startup.InitializationProvider"
  android:authorities="${applicationId}.androidx-startup"
  android:exported="false"
  tools:node="merge">
  <meta-data android:name="com.emergetools.reaper.ReaperInitializer"
    tools:node="remove" />
</provider>

You may need to add a direct dependency on theandroidx.startup:startup-runtime library if the app does not already have such a dependency.

// app-module build.gradle.kts
dependencies {  
  implementation("androidx.startup:startup-runtime:<latest_version>")
}

Follow the Manual initialization steps below to ensure that Reaper is initialized.

Manual initialization

To manually initialize Reaper call Reaper.init(context). For example:

import com.emergetools.reaper.Reaper

class MyApp : Application() {
  override fun onCreate() {
    Reaper.init(this)
  }
}
import com.emergetools.reaper.Reaper;

public class MyApp extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    Reaper.init(this);
  }
}

A common case when manual initialization is required is when androidx.startup.InitializationProvider has been globally disabled by the following snippet being included in the AndroidManifest.xml:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="remove">
  <meta-data
    	android:name="androidx.work.WorkManagerInitializer"
    	android:value="androidx.startup"
    	tools:node="remove" />
</provider>

Common Errors

Missing classes detected while running R8

Example error message

ERROR: Missing classes detected while running R8. Please add the missing classes or apply additional keep rules that are generated in missing_rules.txt.  
ERROR: R8: Missing class com.emergetools.reaper.ReaperInternal (referenced from: void \_COROUTINE.ArtificialStackFrames.<init>() and 5715 other contexts)

Cause This occurs when the Gradle plugin instruments the code but the SDK is not added as a implementation dependency.

Fix Add the following code to the application build.gradle.kts file:

// app-module build.gradle.kts
dependencies {  
  implementation("com.emergetools.reaper:reaper:<latest_version>")
}