Mobile RUM - Android SDK

The Mobile RUM SDK provides a customizable suite of tools to analyze and optimize the performance of Android applications. Isolate ANR and network changes, quickly detect application crashes, identify slow or frozen frames, and more. To see an example of how to deploy the Mobile RUM Android SDK, navigate to our GitHub repository.

Prerequisites

  • Android SDK version: API 21+ (Android 5.0+). Check with:
    1./sdkmanager --list
  • Gradle project with mavenCentral() enabled.
  • Internet reachability to your ingest target domain.

Note (Android 9+, API 28): Cleartext (HTTP) traffic is disabled by default; prefer HTTPS for your APIs and target. For local/dev HTTP, use a scoped Network Security Config (see Troubleshooting).

Install & Instrument Your Android Application

1 Install Android Middleware SDK

Add the SDK to your app module so Gradle can resolve and bundle the Mobile RUM library. This brings in the core telemetry runtime and OpenTelemetry interfaces we rely on. In production, consider pinning to a specific version rather than + so upgrades are intentional; for quick starts, + is fine.

1implementation 'io.middleware.android:sdk:+'
2implementation "io.opentelemetry.instrumentation:opentelemetry-instrumentation-api"

2 Configuration Methods (Optional)

Use the following builder methods to tune what the SDK collects on-device. These switches only affect client-side collection, and they don’t require any server-side changes: Use these methods to tailor ingestion & monitoring:

1import static io.middleware.android.sdk.utils.Constants.APP_VERSION;
2  import io.middleware.android.sdk.Middleware;
3  import io.opentelemetry.api.common.Attributes;
4
5  class MyApplication extends Application {
6    private final String targetUrl = "<target-url>";
7    private final String rumAccessToken = "your-account-token";
8
9    @Override
10    public void onCreate() {
11      super.onCreate();
12
13      Middleware.builder()
14        .setTarget(targetUrl)
15        .setRumAccessToken(rumAccessToken)
16        .setServiceName("sample-android-app-1")
17        .setProjectName("Mobile-SDK-Android")
18        .setDeploymentEnvironment("PROD")
19        .setGlobalAttributes(Attributes.of(APP_VERSION, BuildConfig.VERSION_NAME))
20        // Optional tunables:
21        // .disableCrashReporting()
22        // .disableAnrDetection()
23        // .disableNetworkMonitor()
24        // .disableSlowRenderingDetection()
25        // .setSlowRenderingDetectionPollInterval(Duration.ofMillis(1000))
26        .build(this);
27    }
28  }

Tip: Initialise as early as possible for best coverage of app start and early network calls.

Use these methods to tailor ingestion & monitoring:

MethodDescription
setRumAccessToken(String)Authorizes client to send telemetry to Middleware
setTarget(String)Sets target URL to recieve telemetry
setService(String)Sets service name of your application
setDeploymentEnvironment(String)Sets environment attribute on spans generated by instrumentation. Example: PROD, DEV
disableCrashReporting()Disables crash reporting which is enabled by default
disableAnrDetection()Disables Application Not Responding (ANR) detection which is enabled by default
disableNetworkMonitor()Disables network change detection which is enabled by default
disableSlowRenderingDetection()Disables slow or frozen frame render detection which is enabled by default
setSlowRenderingDetectionPollInterval(Duration)Sets default polling for slow or frozen render detection. Default detection interval is 1000 milliseconds

3 HTTP Instrumentation Config (Optional)

Out of the box, the SDK records UI and system signals. To gain request‑level visibility (method, URL, status, timings), instrument your OkHttp client. If you use Retrofit, wire the instrumented Call.Factory into your Retrofit.Builder. No handler changes are required.

1Integrate with OkHttp3 to monitor HTTP events across user devices.
2private Call.Factory buildOkHttpClient(Middleware middleware) {
3  return middleware.createRumOkHttpCallFactory(new OkHttpClient());
4}

Retrofit: Supply the instrumented Call.Factory to Retrofit.Builder().callFactory(...) so your existing Retrofit stack is covered without further code changes.

Custom Configurations

Set Global Attributes

Add stable keys (release, tenant, plan) that you will filter and chart by. Avoid PII—prefer IDs or hashed values. Setting app.version is especially useful for grouping by release in dashboards and matching source maps.

Attach contextual metadata to all telemetry:

1Middleware.builder()
2  .setGlobalAttributes(
3    Attributes.builder()
4      .put("key", "value")
5      .put(StandardAttributes.APP_VERSION, BuildConfig.VERSION_NAME)
6      .build()
7  );

Use for release, user cohort, tenant, etc.

Events

Use lightweight business events to track user intent (e.g., add‑to‑cart, subscription start) or UX milestones (e.g., onboarding step completed). For multi‑step flows, wrap the sequence with a workflow span. Keep names consistent across releases for trend analysis.

Step 1: Set up Your Custom Event

1Middleware.getInstance().addEvent("You clicked on Button", BUTTON_ATTRIBUES);

Step 2: Start Custom Event Workflow

1Span loginWorkflow = Middleware.getInstance().startWorkflow("User Login Flow");

Step 3: End Custom Event Workflow

1loginWorkflow.end();

Error Reporting

Crashes are captured automatically. Use addException(Throwable) for handled exceptions where your app catches an error, but you still want it visible in dashboards and correlated with the session/replay. Include attributes that help triage (feature flag, screen name, request id).

Use addException(Throwable) to report exceptions, errors, and display messages on the Middleware dashboard.

1Middleware.getInstance().addException(new RuntimeException("Something went wrong!"), Attributes.empty());

Logs

Client logs are enriched with session and device context and can be correlated with errors and replays. Use levels (debug/info/warn/error) to express severity; prefer structured messages and avoid logging secrets. Add custom logs that appear on your Middleware dashboard:

1Middleware logInstance = Middleware.getInstance();
2logInstance.d("TAG", "I am debug");
3logInstance.e("TAG", "I am error");
4logInstance.i("TAG", "I am info");
5logInstance.w("TAG", "I am warning");

Session Replay

Session Replay captures what the user saw and did. Use it to understand regressions and validate fixes. Start and stop capture around your Activity lifecycle so you record only foreground time. Replay data is batched and uploaded efficiently; keep privacy in mind for sensitive screens.

Control how you capture and replay the user experience. Start and stop session replay in your Activity lifecycle:

1final MiddlewareRecorder recorder = Middleware.getInstance().getRecorder();
2
3@RequiresApi(api = Build.VERSION_CODES.N)
4@Override
5protected void onResume() {
6  super.onResume();
7  recorder.startRecording(this);
8}
9
10@RequiresApi(api = Build.VERSION_CODES.N)
11@Override
12protected void onPause() {
13  recorder.stopRecording();
14  super.onPause();
15}

Availability: Session Replay is supported on modern Android versions (enable conditionally per your min/target SDK).

Session Recording

  • Max duration: 4 hours per session.
  • Idle timeout: 15 minutes of inactivity ends a recording; activity after the timeout starts a new session.
  • Default: Recording is enabled; disable via .disableSessionRecording().

Privacy

Masking happens on the device before upload. Mark views that may contain sensitive content (payments, OTPs, tokens) so they are blurred in all recordings. Password fields are masked by default, but you should still review high‑risk screens.

Blur sensitive information in session recordings:

1final Middleware instance = Middleware.getInstance();
2final TextView someTextView = findViewById(R.id.some_text_view);
3instance.addSanitizedElement(someTextView);
  • Passwords are masked automatically by default.
  • Mask other sensitive UI (payments, tokens) by adding specific views.

Consent (optional): If your app requests user consent, you may choose to disable Session Recording when consent is not granted (see Troubleshooting).

Default Attributes

These tags are added automatically and appear in query builders and dashboards. Use them to slice data by app, service, or session.

The following Attributes are provided by the Android SDK by default:

MethodTypeDescription
project.name, appStringDefines the project name, used as projectName(String)
service.nameStringDefines the service name, used as serviceName(String)
session.idStringRandom session identifier generated by Middleware SDK
rum.sdk.versionStringMiddleware SDK version

Resource Attributes

Device and OS metadata help you spot model‑specific or version‑specific issues. When investigating performance, start by grouping by device.model.name and os.version.

Applied to all spans by default:

NameTypeDescription
envStringName of deployment environment. Example: DEV, PROD
device.model.identifierStringDevice model identifier. Example: Moto-G30
device.model.nameStringName of device. Example: ONEPLUS A600
device.manufacturerStringName of device manufacturer. Example: OnePlus
os.nameStringOperating system name. Set to Android
os.descriptionStringOS description. Example: Android Version 11 (Build RKQ1.201217.002 API level 30)
os.typeStringOS type. Set to Linux
os.versionStringOS version. Example: 11

Instrumentation Attributes

Crash Reporting

Crash Reporting is enabled by default and adds the following attributes to spans representing uncaught exceptions:

NameTypeDescription
thread.idIntegerID of the current managed thread (not OS thread ID)
thread.nameStringName of the thread
exception.messageStringException message
exception.typeStringException type
exception.stacktraceStringStack trace for the exception
exception.escapedStringtrue for uncaught exceptions (crash)
componentStringcrash
event.typeStringerror

Network Monitoring

Produces spans named network.change with:

NameTypeDescription
network.statusStringlost or available
network.connection.typeStringwifi, cell, unavailable, or unknown
network.carrier.nameStringCarrier name

Application Not Responding (ANR)

ANRs indicate the UI thread was blocked long enough for Android to flag the app as unresponsive. The SDK emits a span when this threshold is crossed so you can correlate with recent network calls, disk I/O, or heavy work.

ANR detection creates spans whenever the main thread is unresponsive for > 5 s. Enabled by default. Attributes:

NameTypeDescription
exception.stacktraceStringStack trace for the ANR
componentStringerror
event.typeStringerror

Slow Rendering Detection

Jank degrades perceived quality. We report slow (>16 ms) and frozen (>700 ms) frames so you can track UI smoothness over time and catch regressions early. Use the poll interval to balance fidelity and overhead.

Slow rendering produces spans when a frame is > 16 ms; frozen when > 700 ms. During each interval, two spans can be emitted: slowRenders (slow frames) and frozenRenders (frozen frames). Enabled by default.

NameTypeDescription
countIntegerNumber of slow/frozen frames in a 1s interval (interval adjustable via slowRenderingDetectionPollInterval)

HTTP Client Attributes

The Android RUM agent instruments OkHttp. Activate via Step 3 above. Attributes:

NameTypeDescription
http.methodStringe.g., GET, POST, HEAD
http.urlStringe.g., https://foo.bar/address?q=value#hash
http.flavorStringe.g., 1.0
http.status_codeIntegere.g., 200, 404, 418
http.response_content_lengthIntegere.g., 3495 (bytes)
http.user_agentStringe.g., CERN-LineMode/2.15 libwww/2.17b3
net.transportStringe.g., IP.TCP
net.peer.nameStringe.g., example.com
net.peer.portIntegere.g., 80, 8080, 443
componentStringhttp

Activity Lifecycle Monitoring

Enabled by default. Generates spans whenever an Activity changes state.

NameTypeDescription
componentStringui
activityName, activity.nameStringActivity class name (e.g., MainActivity)

Fragment Lifecycle Monitoring

Generates spans whenever a Fragment changes state. Possible states include: Created, Restarted, Resumed, Paused, Stopped, Destroyed.

NameTypeDescription
componentStringui
fragmentNameStringFragment class name (e.g., MainFragment)

App Start Monitoring

Creates spans on Cold, Warm, and Hot app starts.

  • Cold: App launched fresh after boot/kill
  • Warm: Partial init still required; faster than cold
  • Hot: App brought to foreground; already loaded
NameTypeDescription
componentStringappstart
start.typeStringOne of: cold, warm, hot

Android‑specific Notes (Additive context)

The following platform specifics are commonly required in production builds.

Manifest permissions

1<uses-permission android:name="android.permission.INTERNET"/>
2<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Network Security Config & HTTPS (API 28+)

  • On Android 9+ (API 28), cleartext HTTP is blocked by default. Prefer HTTPS for APIs and the Middleware target.
  • For local/dev HTTP, create a Network Security Config (domain‑scoped) and/or use usesCleartextTraffic only for permitted hosts.

Retrofit/OkHttp integration tips

  • Prefer a single shared OkHttpClient; if multiple clients exist, wrap each via createRumOkHttpCallFactory(...).
  • For Retrofit, either pass a custom Call.Factory or use client(OkHttpClient) which sets the call factory internally.

If your app shows a privacy/consent screen, you may initialise Middleware after consent or disable Session Recording when consent is not granted:

1if (userConsented) {
2  // normal builder as shown above
3} else {
4  Middleware.builder()
5    .setTarget(targetUrl)
6    .setRumAccessToken(rumAccessToken)
7    .disableSessionRecording()
8    .build(this);
9}

Troubleshooting

Start here if data isn’t appearing or specific signals are missing.

  • No data / network errors: Verify your target URL and HTTPS; on API 28+ cleartext HTTP is blocked unless explicitly allowed via Network Security Config.
  • No HTTP events in Retrofit: Ensure your Retrofit callFactory or client(OkHttpClient) uses the instrumented OkHttp instance.
  • Replay not starting: Confirm OS version supports replay and that lifecycle hooks call startRecording/stopRecording.
  • High frozen/slow frame counts: Reduce main‑thread work. Slow > 16 ms, Frozen > 700 ms.

Need assistance or want to learn more about Middleware? Contact our support team at [email protected] or join our Slack channel.