The Android client for Arizona's
?native (JSON) render target — the native counterpart of the browser client in
arizona's assets/js/. The wire protocol it speaks is defined by arizona's
include/arizona_wire.hrl and include/arizona_effect.hrl; make check-wire
compares this client's codes against them.
It is built and tested against the arizona release pinned in .arizona-version, so an
arizona change never breaks it on its own: moving to a newer arizona is a PR here that
bumps the pin (Renovate opens one per arizona release), and CI checks it.
Architecturally this is React Native's renderer layer, fed by the server over a WebSocket instead of by on-device JS: the server holds all state and logic and ships a cached-statics + diff stream; this thin client reconstructs a Jetpack Compose widget tree and relays events back. No app logic runs on the device, and nothing is compiled to native ahead of time — the UI is real Compose widgets built at runtime from the server's JSON.
It mirrors the proven JS reference client (e2e/utils/native_client.js in the
Arizona repo): connect → cache statics by fingerprint → interleave statics+dynamics
(JSON-encoding each value) → flatten #slot fragments → apply ops against an
az → node registry → send events.
:arizona— the client library:Wire.kt— op codes +FingerprintCache+Interleaver(pure, JVM-testable).Tree.kt— the reactiveNodemodel (Compose snapshot state) + parse +azindex.AzClient.kt— OkHttp WebSocket, op application,pushEvent.Renderer.kt—WidgetRegistry+RenderContext+ArizonaView(vocabulary-agnostic Compose dispatch).
:sample— a demo app: registers aColumn/Row/Text/Buttonvocabulary and renders the server's/native/counterview.
Authored against a known-good late-2024/early-2025 toolchain (see gradle/libs.versions.toml);
align versions with your installed Android SDK/AGP/Kotlin. The package names
(dev.arizona.*) are placeholders — rename freely. No Gradle wrapper is committed:
build via Android Studio (it bundles its own Gradle) or a system gradle install —
the CLI commands below assume gradle on PATH. CI provisions Java and Gradle with
mise, from the versions pinned in .tool-versions.
Pure logic (no emulator) — validates the interleaver against the real captured frame:
make test # gradle :arizona:testDebugUnitTestWire codes — against the pinned arizona, cloned into _arizona by make arizona
(it must be compiled, which make server does; ARIZONA_DIR=../arizona points at
another checkout):
make arizona # once per pin
make check-wireRun the demo on an emulator or device — start the server, tunnel the device's
localhost:4040 to it with adb reverse (works for the emulator and a real
device alike, over USB; no LAN IP), then launch the app:
# arizona's e2e test server on 0.0.0.0:4040, from the pinned checkout:
make server
# With a running emulator/device (re-run after replugging the cable):
adb reverse tcp:4040 tcp:4040
# Or just hit Run in Studio:
gradle :sample:installDebug # then open the appThe app opens a menu; tap a button to navigate to each example
(counter, list, tabs, ticker, multi), and ☰ Menu to go back. The app connects
to http://localhost:4040 (the adb reverse tunnel).
On-device e2e (the Playwright analogue) — with the server running:
make e2e # gradle :sample:connectedCheck (tunnels adb reverse itself)CI that wires the server + emulator together lives in .github/workflows/ci.yml.