Crash reporting is on by default. One Everframe.start call is enough; there is nothing else to enable. It reports uncaught JVM exceptions, including OutOfMemoryError, on every Android version the SDK supports.
It also reports native crashes, ANRs, and low-memory kills while the user could see or hear your app. For those, the SDK picks the mechanism by Android version:
| Android | JVM exceptions | Native crashes (SIGSEGV, abort) | ANRs | Low-memory kills |
|---|---|---|---|---|
| 7–10 (API 24–29) | yes | not captured | not captured | not detected |
| 11 (API 30) | yes | yes, without stack frames | yes | yes, in the foreground or during playback |
| 12+ (API 31+) | yes | yes, with the native stack from the OS tombstone | yes | yes, in the foreground or during playback |
It is a client veto — one switch turns all of it off:
EverframeConfig(appId = "<App ID>", sdkKey = "evf_live_…", capture = CaptureConfig(crash = false))
Delivery is on the next launch
A process that has died cannot upload anything. The report is written to the outbox and sent when your app next starts, which has one consequence worth building your triage around:
occurredAt and submittedAt are different times, sometimes by days. Order
on occurredAt. A crash loop otherwise looks like it all happened at once, at
the moment the user finally reopened the app.
requestOutboxDrain() asks the outbox to flush immediately rather than waiting
for its own schedule — useful right after startup if you want crash reports
promptly.
Report a caught exception
Handled Kotlin and Java failures can be reported too:
import dev.everframe.CaptureExceptionOptions
import dev.everframe.ErrorSeverity
import dev.everframe.Everframe
Everframe.captureException(
error,
CaptureExceptionOptions(
severity = ErrorSeverity.WARNING,
context = "checkout.save-cart",
metadata = mapOf("attempt" to 2),
),
)
The one-argument Everframe.captureException(error) form defaults to error
severity. Both forms record handled: true, fatal: false, and mechanism
captureException without opening UI or terminating the app. They make a
bounded, best-effort encrypted outbox write, request a drain after acceptance,
and return before delivery is confirmed. capture.crash = false suppresses
both explicit and automatic error capture.
What arrives
{
"exceptionType": "java.lang.NullPointerException",
"message": "Attempt to invoke virtual method 'int java.util.List.size()' on a null object reference",
"frames": [
{
"raw": "at com.example.CheckoutViewModel.total(CheckoutViewModel.kt:42)",
"file": "CheckoutViewModel.kt",
"function": "total",
"line": 42
}
],
"mechanism": "uncaught-exception-handler",
"handled": false,
"occurredAt": "2026-06-15T11:59:58.400Z",
"fingerprint": "9f2c1ab30d84e177"
}
The envelope keeps Raw captured frames. For an optimized build, the
dev.everframe Gradle plugin uploads the build’s final mapping.txt after
assembleRelease/bundleRelease and packages the matching r8MappingId,
which the SDK reports (see Android R8). When
retracing succeeds, the error detail opens
in Retraced mode; Raw remains available.
mechanism is a plain string, not an enum, so v2 can add values without older
receivers rejecting reports.
Native crashes and ANRs
On Android 11 and later the OS records why your previous process ended. On the next launch the SDK reads that record and sends a native crash or an ANR report for it, with the frozen app version and destination of the process that died. ANR reports carry the main thread’s frames from the OS trace. Native crash reports on Android 12+ carry the native frames from the OS tombstone.
Native and ANR capture run in your app’s main process. Other processes report JVM exceptions only. Capture begins a moment after Everframe.start, once its background setup completes; a native crash or ANR before that is not reported.
User force-stops and other exits are not crashes, and the SDK does not report them. A JVM crash is reported once, by the exception handler, not again from the OS record.
The SDK uses ActivityManager.setProcessStateSummary to recognise its own process. Do not call it in your app. If another library does, exits it overwrote are not reported, and the SDK logs an Everframe warning naming process-state-summary-conflict. A library that writes the summary before the SDK starts is overwritten silently.
If a crash is missing from Everframe but visible in Play Console vitals, check the Android version first: native crashes, ANRs and low-memory kills before Android 11 are not captured.
R8 retracing applies to Java and Kotlin JVM frames. Native crash frames are mapped with native library symbols, uploaded as described in Android native libraries.
Low-memory kills
When the OS ends your app for low memory while it is in the foreground, playing audio or video from a foreground service, or visible (picture-in-picture playback included), the next launch reports one fatal Low memory kill. It has no stack, so all of them group into one issue per app across releases, and each app in a project has its own; each report carries the app state at death, the memory the OS recorded (PSS/RSS, when it recorded any), the OS exit description, the time and the release. Release health counts it like an ANR. Low-memory kills in the background, of a cached app, or while the app was only perceptible (Android also reports that for expedited jobs and backups the user never saw) are normal Android lifecycle and are not reported.
When the JVM crash handler already reported the crash that ended the process, such as a Java OutOfMemoryError, a low-memory kill that finishes it is the same death: it is not reported again, and release health counts that launch as the crash.
The SDK reports only exits the OS records as low-memory kills. On devices where ActivityManager.isLowMemoryKillReportSupported() is false, or when the kernel ends the app before Android’s low-memory killer does (the exit is then recorded as a plain kill signal), it cannot tell them apart and reports nothing; it never guesses from a kill signal. Before Android 11 low-memory kills are not detected. A Java OutOfMemoryError is an ordinary JVM crash report.
Chained handlers
The SDK installs its handler and calls through to whatever was already installed, so adding Everframe does not displace Crashlytics or your own handler. Order matters only in that Everframe records first, which is what lets it capture the breadcrumb trail before another handler terminates the process.
Grouping
fingerprint is 16 hex characters computed on the client from the exception
type and the top frames — a heuristic, deliberately, so grouping works with no
server round-trip and no deobfuscation step. Treat it as a strong hint, not an
identity.
Once a build’s R8 mapping and native libraries are uploaded, its crashes are
grouped by the mapped function, so the same crash in a new build joins its
existing issue. A crash whose top frames have no mapped line, such as one
inside a Kotlin lambda, keeps an issue per build for now. Until then, issues
follow fingerprint. See Android R8 for the
uploads.
What comes with it
An unattended report has no description — nobody typed one. Its title is the exception type and message. What makes it useful is the breadcrumb trail leading to the throw — console, network, taps and the screen the user was on — plus device metadata and the user, if one was set.
It carries no screenshot and no replay: the handler runs synchronously in a dying process, and only what is already in memory as breadcrumbs is written out.