Android
Capture Android identity, events, errors, crashes, and backend request context.
On this page
The native Kotlin SDK supports Android minSdk 24 and has no third-party runtime dependency. During early access, add the artifact and repository configuration supplied during onboarding.
Add the dependency
During early access, copy the supplied AAR into your app module's libs/anectico-sdk.aar, then add:
dependencies {
implementation(files("libs/anectico-sdk.aar"))
// Optional when using the Anectico OkHttp interceptor:
implementation("com.squareup.okhttp3:okhttp:4.12.0")
}
After the public Maven release, replace the local AAR with the exact published
com.anectico:sdk:<version> coordinate supplied for your release.
The SDK's INTERNET permission is merged into the application manifest.
Initialize once
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Anectico.init(
this,
AnecticoOptions(
apiKey = BuildConfig.ANECTICO_API_KEY,
environment = "production",
release = "com.acme.shop@2.4.1+241",
dist = "241",
enableCrashReporting = true,
debug = BuildConfig.DEBUG,
),
)
}
}
serviceName defaults to the application ID and serviceVersion to versionName.
init reports failure rather than absorbing it — IllegalArgumentException for invalid
configuration, AnecticoInitException (with the cause) for anything else that stops setup — so an
app that starts is an app whose telemetry is really running. Methods called before a successful
init record and send nothing: a debug build throws AnecticoNotInitializedException at the call
site, and a release build logs the first one at ERROR. See the
Android SDK API reference.
Use a project-scoped key with ingest:write and analytics:write. Do not include read, management,
or agent scopes in the application.
Connect identity and events
Anectico.identify("user_8842", mapOf("email" to "buyer@acme.example", "plan" to "pro"))
Anectico.group("company", "acme", mapOf("plan" to "enterprise"))
Anectico.capture("checkout_started", mapOf("cart_size" to 3))
Anectico.screen("Checkout")
Anectico.captureLog(
"checkout prepared for offline sync",
severity = "info",
attributes = mapOf("items" to 3),
)
// On logout:
Anectico.reset()
reset also clears global error-user context and buffered breadcrumbs, preventing
the prior account's diagnostic context from attaching to the next account on a
shared device.
Every captured event — not only the automatic $app_opened/$app_backgrounded lifecycle
events — carries the reserved $release and $app_version properties, sourced from the same
release/serviceVersion you pass to AnecticoOptions (or versionName when you don't set
them explicitly). Neither is invented: an app that never configures a release sends events with
neither property. See Reserved event properties.
captureLog sends OTLP logs to /v1/logs with the current customer/session and Android
app/device/release context. Logs are atomically persisted before delivery, retain their occurrence
time and stable anectico.log.id across process recreation, and retry network/429/5xx failures in
oldest-first order. Anectico.flush flushes both events and logs.
maxQueueSize is applied separately: the SDK can retain that many diagnostic events and that many
logs, rather than splitting one count between them. The log spool also drops oldest past 10 MiB.
Capture errors and upload mappings
try {
checkout()
} catch (error: Exception) {
Anectico.captureError(error, CaptureOptions(tags = mapOf("screen" to "checkout")))
}
Anectico classifies explicit captureError calls as severity error, handled, with mechanism
java.caught_exception. The uncaught-exception handler classifies fatal crashes as severity
fatal, unhandled, with mechanism java.uncaught_exception. Those values are SDK-owned and cannot
be replaced by capture tags; unrelated application tags still appear on the occurrence.
The occurrence also includes SDK-owned app version/build, Android model and OS version, and Anectico SDK/platform context for Issue Story. These are shared runtime descriptors, not physical-device identifiers; the SDK never collects Android ID, serial number, installation ID, or the user-assigned device name.
Fatal exceptions and failed manual error sends are spooled and retried on the next launch. Upload
the exact R8/ProGuard mapping for the captured release and dist:
anectico symbols upload-proguard mapping.txt --release "$RELEASE" --dist "$DIST"
The Issue Story identifies missing or mismatched mappings beside the raw R8 frames and shows the
exact release/distribution keys to upload. An empty distribution is a real key; preserve it with
--dist ''. After a mapping resolves, each recovered Original source entry remains paired with
a Raw frame line containing the captured obfuscated function and location; R8 inline entries
repeat that raw evidence in mapping order. The upload key requires errors:write.
Propagate to your backend
For OkHttp:
val client = OkHttpClient.Builder()
.addInterceptor(com.anectico.sdk.okhttp.AnecticoInterceptor())
.build()
For HttpURLConnection, call AnecticoPropagation.apply(connection). Send these headers only to
trusted application services.
Verify
Identify a test user, capture one event and caught error, call Anectico.flush, and confirm the evidence
shares one customer. Test a fatal crash only in a safe test build, then relaunch so the spool drains.