Source: https://docs.any.org/quickstart/android.html # Android On Android the server does not run as a separate process — it is a library. `any.aar` is a gomobile binding of the same server; your app starts it in-process, gets a loopback address back, and uses plain HTTP from there. ## Get the artifact Every release publishes `any.aar` (arm64-v8a) alongside the desktop tarballs, with its sha256 in the release notes. Build it yourself with an Android NDK on the path: ```bash make build-android # → dist/android/any.aar ``` Drop it into `app/libs/` and add it as a dependency: ```kotlin // build.gradle.kts dependencies { implementation(files("libs/any.aar")) } ``` ## The surface The binding is deliberately flat — top-level functions, strings and errors only: | Function | Purpose | | --- | --- | | `Mobile.start(dataDir, listenAddr, nodeconfYAML)` | Boot the server. Returns once the listener is bound; throws on wallet / SDK / listener failure with the underlying message. A second call while running throws. | | `Mobile.startWithPush(dataDir, listenAddr, nodeconfYAML, pushPeerId, pushAddrs)` | Same, plus the push-notification node (`pushAddrs` comma-separated). Empty strings = no push. | | `Mobile.startWithMode(…, mode, controlToken)` | `startWithPush` plus the ownership mode. `"managed"` makes the app the owner: the account arrives over `POST /v1/auth` on every launch, keys never touch disk, and sign-out / switch / shutdown need `controlToken` (`X-Any-Control-Token`). `""` or `"standalone"` is the `start` behavior; `"managed"` without a token throws. | | `Mobile.address()` | The actually bound `host:port`. Differs from `listenAddr` when you passed `:0`. Empty when not running. | | `Mobile.stop()` | Graceful shutdown; waits for the server to exit. Safe when not running. | | `Mobile.stopNow()` | Hard stop, returns promptly — for a deadline-bounded teardown. | | `Mobile.version()` | Build-stamped version string. | `nodeconfYAML` is the network: pass `""` for the production any-sync network embedded in the AAR, or a nodeconf's YAML text to join another one ([Networks](https://docs.any.org/quickstart/networks.md.txt)). The AAR runs the index with no embedder, so `/search` runs `fts` only. ## Start it ```kotlin import mobile.Mobile class AnyService : Service() { override fun onCreate() { super.onCreate() Thread { Mobile.start(filesDir.absolutePath, "127.0.0.1:0", "") // OS picks a free port val addr = Mobile.address() // e.g. 127.0.0.1:41823 AnyClient.baseUrl = "http://$addr/v1" }.start() } override fun onDestroy() { Mobile.stop(); super.onDestroy() } } ``` Use `filesDir` (or another app-private directory) as the data dir: it holds the wallet, the databases, files, and the search index for the account ([Data dir](https://docs.any.org/operations/data-dir.md.txt)). ## Create or restore the account An embedded server starts *unauthorized* on a fresh data dir — there is no `any init` on a phone. Onboard over HTTP: ```kotlin // generate a new account; the mnemonic comes back ONCE — show it and make the user save it POST http://$addr/v1/auth {} // → { "accountId": "A8g1…", "created": true, "mnemonic": "w1 … w12" } // or restore from a phrase (same account id, fresh device key) POST http://$addr/v1/auth { "mnemonic": "w1 … w12" } ``` Until then every route except `/v1/health`, `/v1/shutdown`, `/v1/openapi.json`, `/v1/auth` answers `401 auth.required`. On later launches a standalone server finds the wallet in `filesDir` and boots the engine on `start`; a managed one waits for `POST /v1/auth` again ([Accounts](https://docs.any.org/auth/accounts.md.txt)). ## Then it is just HTTP ```kotlin val client = OkHttpClient() // "page": the built-in document type val body = """{"type":"page","initialProperties":{"any":{"name":"From Android"}}}""" val req = Request.Builder() .url("${AnyClient.baseUrl}/spaces/$space/objects") .post(body.toRequestBody("application/json".toMediaType())) .build() client.newCall(req).execute().use { println(it.body!!.string()) } // {"objectId":"bafy…"} ``` Live reads are the same streaming POST as everywhere else — read the response body line by line and split on blank lines ([JavaScript](https://docs.any.org/quickstart/javascript.md.txt) shows the parser; the frame set is in [Subscriptions](https://docs.any.org/realtime/subscribe.md.txt)). ## Lifecycle notes - **One server per process.** `start` while running throws; restart on the same data dir is serialized by `stop`. - **Background limits.** Use `stopNow()` from a deadline-bounded callback; `stop()` drains open streams with a 10 s ceiling. - **Push.** Pass the push node to `startWithPush` and cache each space's `SpaceInfo.push` keys in the Keystore so a notification extension can decrypt while the server is not running ([Push](https://docs.any.org/notifications/push.md.txt)). > **Why it matters.** The phone holds the whole database. There is no "mobile API" that returns less than the desktop one, no offline cache to invalidate, and no request that fails because the network is down — the app talks to `127.0.0.1` and sync happens whenever it can.