The SDK keeps a rolling window of what the app rendered in memory. When a report is
filed that window is frozen and attached; the rest is discarded. Nothing is
uploaded unless someone files a report, and there is no MediaProjection
permission prompt — this is not screen recording.
Turning it on
Server-gated — enable it for the app in the dashboard, and the SDK picks it up
from GET /api/config on the next start.
There is no client-side switch for replay on Android — the server gate is the only control. If you need it off for a particular build, turn it off for that app in the dashboard.
Crash-only sessions
An app can turn the on-device reporter off with EverframeConfig(..., reporterEnabled = false) to report crashes
from production builds without recording every session. Each session is one of
two kinds, and the Session replay card in the dashboard sets a replay
coverage for each:
| Session | Reporter | Replay coverage |
|---|---|---|
| Report session | On | Sessions that can file a report (samplingRate) |
| Crash-only session | Off | Crash-only sessions (crashOnlySamplingRate, default 0%) |
Each session draws once, when it starts, and keeps the result: a later rate change applies to new sessions, not to ones already running.
The on-device reporter stays off for the whole session: report.open() throws.
An error carries a replay only while its session records. At the default crash-only coverage of 0%, errors from crash-only sessions carry no replay. At 5%, errors from about 5% of crash-only sessions do. See Configuration for the switch.
Format
Android replays are everframe-vtree-v1 — a frame timeline, not video. A
viewport, a list of frames, and per-frame operations on a lightweight node tree
carrying role, frame, text, bg, alpha, cornerRadius and masked.
That keeps a replay in the tens of kilobytes rather than the megabytes video costs, and it means text stays text — searchable, and redactable before it leaves the device. No video encoder runs, which is the difference between a buffer you can leave on and one you cannot, especially on low-end hardware.
Both Compose and classic View hierarchies are captured; the recorder works on the rendered tree, not on your composables.
Images
Image bytes are optional and content-addressed, keyed by the first 16 hex
characters of their sha256, so the same drawable across many frames costs one
entry. The server’s asset mode (off, bundled, all) decides whether they
travel; see Server config.
An unresolved image renders as a placeholder box rather than failing the replay.
Privacy
Sensitive views — Modifier.txSensitive(), TXSensitiveView,
markSensitive() and password fields — become a single node with masked: true
and no text; the subtree beneath is dropped. The masking happens in the
producer, before the frame is written — masked content never exists in the
buffer. See Sensitive content.
Compose text is captured: the text of a Text composable lands in the node,
redacted. The contents of text fields are never read, so what a user types is
not in the replay unless another composable displays it.
Cost
Frames are captured on the choreographer’s cadence and diffed, so a static
screen costs almost nothing. replayDurationSec bounds the window;
samplingRate decides what fraction of sessions allocate a buffer at all.