# Measure Android SDK * [Requirements](#requirements) * [Quick start](#quick-start) * [Verify integration](#verify-integration) * [Manually start/stop the SDK](#manually-start-or-stop-the-sdk) * [Features](#features) * [Automatic collection](#automatic-collection) * [Identify users](#identify-users) * [Track custom events](#track-custom-events) * [Performance tracing](#performance-tracing) * [Handled exceptions](#handled-exceptions) * [Screen views](#screen-view) * [Configuration options](#configuration-options) * [Concepts](#concepts) * [Sampling](#sampling) * [Session](#session) * [Performance impact](#performance-impact) # Requirements ### Minimum requirements | Name | Version | |-----------------------|---------------| | Android Gradle Plugin | 7.4 | | Min SDK | 21 (Lollipop) | | Target SDK | 31 | ### Self-host compatibility Before updating to Android SDK version 0.9.0, make sure the deployed self-host version is *atleast* 0.5.0. For more details, checkout the [self-host guide](../hosting/README.md). | SDK version | Minimum required self-host version | |---------------|------------------------------------| | 0.1.0 - 0.8.2 | 0.1.1 | | 0.9.0 | 0.5.0 | # Quick start Once you have access to the dashboard, create a new app and follow the steps below: ### 1. Add the API Key & API URL Copy the API Key and API URL from the dashboard and add it to `AndroidManifest.xml` file. ```xml ```
Configure API Keys for different build types You can also use [manifestPlaceholders](https://developer.android.com/build/manage-manifests#inject_build_variables_into_the_manifest) to configure different values for different build types or flavors. In the `build.gradle.kts` file: ```kotlin android { buildTypes { debug { manifestPlaceholders["measureApiKey"] = "YOUR_API_KEY" manifestPlaceholders["measureUrlKey"] = "API_URL" } release { manifestPlaceholders["measureApiKey"] = "YOUR_API_KEY" manifestPlaceholders["measureUrlKey"] = "API_URL" } } } ``` or in the `build.gradle` file: ```groovy android { buildTypes { debug { manifestPlaceholders = ["measureApiKey": "YOUR_API_KEY"] manifestPlaceholders = ["measureUrlKey": "API_URL"] } release { manifestPlaceholders = ["measureApiKey": "YOUR_API_KEY"] manifestPlaceholders = ["measureUrlKey": "API_URL"] } } } ``` Then add the following in the `AndroidManifest.xml` file: ```xml ```
### 2. Add the Measure gradle plugin Add the following plugin to your project. ```kotlin plugins { id("sh.measure.android.gradle") version "0.7.0" } ``` or, use the following if you're using `build.gradle`. ```groovy plugins { id 'sh.measure.android.gradle' version '0.7.0' } ``` [Read](gradle-plugin.md) more about Measure gradle plugin.
Configure variants By default, the plugin is applied to all variants. To disable plugin for specific variants, use the `measure` block in your build file. > [!IMPORTANT] > Setting `enabled` to `false` will disable the plugin for that variant. This prevents the > plugin to collect `mapping.txt` file and other build information about the app. Features like > tracking app size, de-obfuscating stack traces, etc. will not work. For example to disable the plugin for `debug` variants, add the following to your `build.gradle.kts` file: ```kotlin measure { variantFilter { if (name.contains("debug")) { enabled = false } } } ``` or in the `build.gradle` file: ```groovy measure { variantFilter { if (name.contains("debug")) { enabled = false } } } ```
### 3. Add Measure SDK Add the following to your app's `build.gradle.kts`file. ```kotlin implementation("sh.measure:measure-android:0.9.0") ``` or, add the following to your app's `build.gradle`file. ```groovy implementation 'sh.measure:measure-android:0.9.0' ``` ### 4. Initialize the SDK Add the following to your app's Application class `onCreate` method. > [!IMPORTANT] > To be able to detect early crashes and accurate launch time metrics, > initialize the SDK as soon as possible in Application `onCreate` method. ```kotlin Measure.init(context, MeasureConfig( // Set to 1 to track all sessions, useful for debug builds. samplingRateForErrorFreeSessions = 1f, ) ) ``` # Verify integration Launch the app and use it. Data is synced to server every 30 seconds or when the app goes to background. To be sure a sync is triggered, try killing and reopening the app. Launch the dashboard, you must be able to see some data coming in. Checkout the sessions page. 🎉 Congratulations, you have successfully integrated Measure into your app! ### Troubleshoot If you see no data on the dashboard. Here's how to investigate: #### Enable logs Enable logging during SDK initialization. All Meaure SDK logs use the tag `Measure`. ```kotlin MeasureConfig(enableLogging = true) ``` #### Verify missing configuration If logs show any of the following errors, then review [Step 1: Add API Key & URL](#1-add-the-api-key--api-url). ``` sh.measure.android.API_URL is missing in the manifest sh.measure.android.API_KEY is missing in the manifest ``` #### Verify sampling rate Try setting `samplingRateForErrorFreeSessions` to 1, which would enforce all sessions to be sent to the server. It's typically a good idea to set this to `1` for debug builds. #### Verify server connection If logs contain `Failed to send batch` or `Request failed with unknown error`: * Verify the API_URL in the AndroidManifest is correct * Check server status to ensure it is reachable In case you face any issue, feel free to reach out to us on [Discord](https://discord.gg/f6zGkBCt42). # Manually start or stop the SDK By default, `Measure.init` starts collection of events. To delay start to a different point in your app use [configuration options](configuration-options.md#autostart). This can be used to control the scope of where Measure is active in your application. ```kotlin Measure.init( context, MeasureConfig( // delay starting of collection autoStart = false, ) ) // Start collecting Measure.start() // Stop collecting Measure.stop() ``` > [!IMPORTANT] > Some SDK instrumentation remains active even when stopped. This is to maintain state and ensure seamless data collection when it is started. However, no actual data is collected or sent to the server when the SDK is stopped. # Features Measure SDK operates on an event-based architecture, automatically collecting key debugging events while letting you track custom events, performance traces, screenshots and layout snapshots, etc. Read along for more details. ### Automatic collection The following data is automatically collected by Measure. Read the individual docs for more details. * [Crash tracking](features/feature_crash_tracking.md) * [ANR tracking](features/feature_anr_tracking.md) * [Network monitoring](features/feature_network_monitoring.md) * [Network changes](features/feature_network_changes.md) * [Gesture tracking](features/feature_gesture_tracking.md) * [Layout Snapshots](features/feature_layout_snapshots.md) * [Navigation & Lifecycle](features/feature_navigation_and_lifecycle.md) * [App launch](features/feature_app_launch.md) * [App exit info](features/feature_app_exit_info.md) * [CPU monitoring](features/feature_cpu_monitoring.md) * [Memory monitoring](features/feature_memory_monitoring.md) * [App size](features/feature_app_size.md) ## Identify users Corelating sessions with users is crutial for debugging certain issues. Measure allows setting a user ID which can then be used to query sessions and events on the dashboard. User Id is persisted across app launches. ```kotlin Measure.setUserId("user-id") ``` To clear a user ID. ```kotlin Measure.clearUserId() ``` > It is recommended to avoid the use of PII (Personally Identifiable Information) in the user ID like email, phone number or any other sensitive information. Instead, use a hashed or anonymized user ID to protect user privacy. ## Track custom events Custom events provide more context on top of automatically collected events. They provide the context specific to the app to debug issues and analyze impact. To track a custom event use `trackEvent` method. ```kotlin Measure.trackEvent("event_name") ``` A custom event can also contain attributes which are key value paris. - Attribute keys must be strings with max length of 256 chars. - Attribute values must be one of the primitive types: int, long, double, float or boolean. - String attribute values can have a max length of 256 chars. ```kotlin val attributes = AttributesBuilder() .put("is_premium_user", true) .build() Measure.trackEvent("event_name", attributes = attributes) ``` A custom event can also be triggered with a timestamp to allow tracking events which might have happened before the app or SDK was initialized. The timestamp must be in format milliseconds since epoch. ```kotlin Measure.trackEvent("event_name", timestamp = 1734443973879L) ``` ### Performance tracing Use the [performance tracing](features/feature_performance_tracing.md) APIs to track performance of any part of your application - API calls, DB queries, any function, user journey, etc. The SDK supports nested spans to track hierarchical operations. Following are some *simplified* examples: Example - track a user flow ```kotlin val onboardingSpan = Measure.startSpan("onboarding-flow") try { val signupSpan = Measure.startSpan("signup", parent = onboardingSpan) userSignup() signupSpan.end() val tutorialSpan = Measure.startSpan("tutorial", parent = onboardingSpan) showTutorial() tutorialSpan.end(SpanStatus.Ok) } finally { onboardingSpan.end(SpanStatus.Error) } ``` This will result in a trace like the following: ``` onboarding-flow ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [2.4s] ✓ ┃ ┣━ signup ━━━━━━━━━ [800ms] ┃ ┗━ tutorial ━━━━━━━━━━━━━━━━ [1.6s] ``` Example - track HTTP calls using an interceptor ```kotlin class HttpInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() val span = Measure.startSpan("${request.method} /${request.url.redact()}") return try { val response = chain.proceed(request) span.end(SpanStatus.SUCCESS) response } catch (e: Exception) { span.end(SpanStatus.ERROR) throw e } } } ``` This will result in a trace like the following: ``` GET /orders ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [2.4s] ✗ ``` For more detailed API documentation and examples, checkout [performance tracing docs](features/feature_performance_tracing.md). ### Handled exceptions To track exceptions which were caught and handled by the app, use the `trackHandledException` method. While your app gracefully recovers from these exceptions, tracking them helps identify potential degraded app experience. ```kotlin try { methodThatThrows() } catch (e: Exception) { Measure.trackHandledException(e) } ``` ### Screen View Measure automatically tracks `screen_view` events for [androidx.navigation](https://developer.android.com/jetpack/androidx/releases/navigation) library. It also tracks [lifecycle_activity](features/feature_navigation_and_lifecycle.md#activity-lifecycle) events and [lifecycle_fragment](features/feature_navigation_and_lifecycle.md#fragment-lifecycle) events. However, `screen_view` events can also be triggered manually using the following method to keep a track of the user flow. ```kotlin Measure.trackScreenView("checkout") ``` # Configuration options See all the [configuration options](configuration-options.md) available. # Concepts ## Sampling Sampling controls what percentage of data is collected and sent to the server, helping balance data quality with system performance and storage costs. #### Session Sampling Set [samplingRateForErrorFreeSessions](configuration-options.md#samplingrateforerrorfreesessions) to control event collection from sessions without errors. By default, the SDK sends all events from crashed sessions to the server, while collecting no events from error-free sessions. * 0.0 — No events from error-free sessions (default) * 0.1 — 10% of error-free sessions * 1.0 — All sessions Session sampling helps optimize data collection for crash and error analysis. #### Trace Sampling [traceSamplingRate](configuration-options.md#tracesamplingrate) controls performance trace collection independently of session sampling. While session sampling determines which session-level events are sent, trace sampling specifically controls performance monitoring data. This separation ensures: - Performance traces are collected based on their own sampling rate. - Critical performance data is captured regardless of session errors. - Session data remains focused on crash analysis and debugging. ## Session A session represents a continuous period of activity in the app. A new session begins when an app is launched for the first time, or when there's been no activity for a 20-minute period. A single session can continue across multiple app background and foreground events; brief interruptions will not cause a new session to be created. This approach is helpful when reviewing session replays, as it shows the app switching between background and foreground states within the same session. The current session can be retrived by using `getSessionId` method. ```kotlin val sessionId = Measure.getSessionId() ``` # Performance Impact ## Benchmarks We benchmark the SDK's performance impact using a Pixel 4a running Android 13 (API 33). Each test runs 35 times using macro-benchmark. For detailed methodology, see [android/benchmarks](../../android/benchmarks/README.md). > [!IMPORTANT] > Benchmark results are specific to the device and the app. It is recommended to run the benchmarks > for your app to get results specific to your app. These numbers are published to provide > a reference point and are used internally to detect any performance regressions. Benchmarks results for v0.9.0: * Adds 26.258ms-34.416ms to the app startup time (Time to Initial Display) for a simple app. * Adds 0.57ms for view-based layouts, and 0.65ms for compose based layouts to every gesture. ## Profiling To measure the SDK's impact on your app, we've added traces to key areas of the code. These traces help you track performance using [Macro Benchmark](https://developer.android.com/topic/performance/benchmarking/macrobenchmark-overview) or by using [Perfetto](https://perfetto.dev/docs/quickstart/android-tracing) directly. * `msr-init` — time spent on the main thread while initializing. * `msr-start` — time spent on the main thread when `Measure.start` is called. * `msr-stop` — — time spent on the main thread when `Measure.stop` is called. * `msr-trackEvent` — time spent in storing an event to local storage. Almost all of this time is spent _off_ the main thread. * `msr-trackGesture` — time spent on the main thread to track a gesture. * `msr-generateSvgAttachment` — time spent on background thread to generate a SVG layout.