Android

Native Android SDK - Kotlin-first with full Java interop, covering JVM crashes, NDK native crashes, ANRs, sessions, breadcrumbs and logs.

Requirementslink

  • minSdk 24 (Android 7.0) · compiled against SDK 35 · Java/Kotlin toolchain 17
  • No required runtime dependencies; OkHttp 4.x is compileOnly (only needed if you use the network-breadcrumb interceptor)

1 - Installlink

// settings.gradle.kts - mavenCentral() must be in repositories
// app/build.gradle.kts
dependencies {
    implementation("cloud.newinstance:bugwatch:0.1.1")
}

2 - Initialise (Application.onCreate)link

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        BugWatch.initialize(application = this, options = BugWatchOptions(
            projectId   = BuildConfig.BUGWATCH_PROJECT_ID,
            appSecret   = BuildConfig.BUGWATCH_APP_SECRET,
            environment = if (BuildConfig.DEBUG) "development" else "production",
            release     = BuildConfig.VERSION_NAME,
        ))
    }
}

Keep the credentials in local.properties/CI variables and inject via buildConfigField - never hard-code them in source.

3 - Configuration optionslink

OptionDefaultWhat it does
environmentproductionEvent environment label
release-Version string; must match your symbol uploads
sampleRate1.0Client-side sampling (0–1)
autoSessionTrackingtrueRelease-health sessions
enableAnrTracking / anrThresholdMstrue / 5000ANR watchdog
enableAutoBreadcrumbstrueActivity/lifecycle breadcrumbs
enableNetworkBreadcrumbs + allow/deny host liststrueOkHttp request breadcrumbs
sensitiveFieldsbuilt-in listExtra keys to redact on device
batchSize / flushIntervalMs50 / 5000Upload batching
maxQueueSize1000Offline queue cap
debugfalseSDK diagnostic logging

4 - Capture APIlink

BugWatch.captureException(e)
BugWatch.captureMessage("Sync finished", level = "info")
BugWatch.setUser(User(id = "u_123", email = "ada@example.com"))
BugWatch.setTag("tenant", "acme")
BugWatch.setContext("payment", mapOf("provider" to "paystack"))
BugWatch.addBreadcrumb("Tapped checkout")
BugWatch.okHttpInterceptor()          // add to your OkHttpClient for network breadcrumbs
BugWatch.crashedLastRun               // true if the previous run died
BugWatch.flushBlocking(timeoutMs = 2000)   // before process exit in workers

Automatic capturelink

  • JVM crashes - default uncaught-exception handler (existing handlers are chained, not replaced).
  • NDK native crashes - a C++ signal handler (libbugwatch-native.so, all four ABIs) writes a crash sidecar; the SDK ships it on next launch (processPendingCrash). Frames carry instruction_addr + the module's GNU build-id so the backend symbolicates against your uploaded .so symbols.
  • ANRs - a watchdog reports main-thread stalls past the threshold.

5 - Symbolication (CI)link

npx @newinstance/bugwatch-cli artifacts upload app/build/outputs/mapping/release/mapping.txt \
  --release "$VERSION_NAME" --platform android --type r8 --token "$BUGWATCH_CI_KEY"

npx @newinstance/bugwatch-cli symbols upload app/build/intermediates/merged_native_libs/release \
  --platform android --release "$VERSION_NAME" --token "$BUGWATCH_CI_KEY"

The R8 mapping matches on release + platform - the string must equal options.release exactly. Native symbols match by build-id, so any machine's upload resolves any device's crash of that binary.

Troubleshootinglink

  • No events at all → check debug = true logcat output; verify device clock (tokens reject >60 s skew) and that the credentials belong to this project.
  • Events but unreadable stacks → mapping not uploaded for this exact release, or minification renamed classes after upload - upload in the same CI job that builds the bundle.
  • ANRs flooding → raise anrThresholdMs or sample with beforeSend-style filtering at the dashboard alert level.

The request below is the exact wire call the SDK makes; the token is signed on device from projectId + mobileAppSecret.

POST/api/v1/bugwatch/ingest/mobile

Wire example – Android JVM crash

Android JVM (Kotlin/Java) uncaught exception from the native cloud.newinstance:bugwatch SDK.

exception.stacktrace is an array of obfuscated frames; BugWatch de-obfuscates them server-side using the uploaded R8/ProGuard mapping (see Source Maps & Symbols → Artifact upload · step 1 – presign with artifactType: r8, or bugwatch-cli artifacts upload --type r8).

Auth + token: the same on-device x-bugwatch-token signing as every mobile request.

Headers

x-bugwatch-token

Request body

application/json
{
  "eventId": "bw_e_{{$guid}}",
  "time": {{nowMs}},
  "level": 60,
  "platform": "android",
  "release": "1.0.0",
  "environment": "production",
  "sdk": {
    "name": "bugwatch-android",
    "version": "0.1.1"
  },
  "device": {
    "model": "SM-S901B",
    "family": "Phone",
    "osName": "Android",
    "osVersion": "13",
    "bundleId": "com.example.myapp"
  },
  "exception": {
    "type": "java.lang.NullPointerException",
    "value": "Attempt to invoke virtual method 'void com.example.app.Cart.checkout()' on a null object reference",
    "stacktrace": [
      {
        "filename": "PaymentService.kt",
        "function": "processPayment",
        "lineno": 87,
        "in_app": true
      },
      {
        "filename": "CheckoutActivity.kt",
        "function": "onConfirmClick",
        "lineno": 142,
        "in_app": true
      }
    ]
  },
  "tags": {
    "device_brand": "Samsung",
    "android_api": "33"
  },
  "user": {
    "id": "usr_android_001"
  }
}

Responses

202 – Mobile event ingested

{
  "ingested": 1,
  "skipped": 0,
  "deduped": 0
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/ingest/mobile

Headers

x-bugwatch-token

Request body

Code samples

curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/ingest/mobile' \
  -H 'Content-Type: application/json' \
  --data-raw '{
  "eventId": "bw_e_{{$guid}}",
  "time": {{nowMs}},
  "level": 60,
  "platform": "android",
  "release": "1.0.0",
  "environment": "production",
  "sdk": {
    "name": "bugwatch-android",
    "version": "0.1.1"
  },
  "device": {
    "model": "SM-S901B",
    "family": "Phone",
    "osName": "Android",
    "osVersion": "13",
    "bundleId": "com.example.myapp"
  },
  "exception": {
    "type": "java.lang.NullPointerException",
    "value": "Attempt to invoke virtual method '\''void com.example.app.Cart.checkout()'\'' on a null object reference",
    "stacktrace": [
      {
        "filename": "PaymentService.kt",
        "function": "processPayment",
        "lineno": 87,
        "in_app": true
      },
      {
        "filename": "CheckoutActivity.kt",
        "function": "onConfirmClick",
        "lineno": 142,
        "in_app": true
      }
    ]
  },
  "tags": {
    "device_brand": "Samsung",
    "android_api": "33"
  },
  "user": {
    "id": "usr_android_001"
  }
}'