x‑hakt

A hull that leaves

My setup for web apps assumes a machine that is always on, holding the running thing. A phone app breaks that assumption completely. The app runs on a phone in someone else's pocket, and there is no berth of mine to keep it at. Here is the local Android dev setup I ended up with instead, walked end to end. What runs on the laptop, what the emulator is for, where the build happens, and how a saved file reaches a running app a second later.

workstation infrastructure

exporeact-nativeandroidgradleemulator

I have written up the home dockyard before: a laptop to work on, an always-on old laptop to run things on, a copy of every project’s state somewhere off the box, one private network joining it all. The shape of that setup rests on one idea. The thing you built has to live somewhere, and that somewhere is a machine you keep running.

Then a project needed to be a phone app, and I spent the first hour looking for the machine it lived on. There isn’t one. A phone app does not sit in your harbour waiting for visitors. You build it, you load it aboard a device, and it sails off in someone’s pocket and keeps working whether your laptop is open or shut or at the bottom of a lake. Every instinct from the web setup pointed the wrong way.

So here is the shape I arrived at for building an Android app at home. The stack is with , which is the common way in and the one this note assumes. Words with a dotted underline have a plain explanation on hover, tap or focus.

1 · The app sails without you

The compiled app is the app. Once it is built and installed on a phone, it runs there, on that phone’s own processor, against that phone’s own storage. It works on a plane. Nothing of mine has to be reachable for it to open.

That is the whole reframe, and it takes a while to trust. With a web app the always-on box is the point. Close your laptop and the site stays up, because the site was never on your laptop to begin with. With a phone app there is no equivalent box, because the app is not running on a box at all. What you keep at home is a workshop: the place the drawing happens and the hull gets fitted out. The hull itself leaves.

your laptopthe workshopinstall oncethe apprunningplane · tunnel ·laptop shutno berth of yours is kept
The web setup keeps the running thing in your harbour. A phone app gets loaded aboard and sails; what you keep is the yard it was built in.

2 · The dry dock

You still need somewhere to run the app while you build it, and there are two.

The is a whole phone running as a window on your computer. It boots a real Android system, installs your app, and is enough for most of the day. A real phone plugged in over USB with developer mode on is the other, and it is the one that matters for anything touching real hardware: the camera, the GPS, the motion sensors, the health data. It is also the only way to feel what a tester will feel. I keep both to hand and reach for the emulator by default. is the tool that talks to whichever is connected, and adb devices is the first thing you run when nothing is showing up.

The machine that runs the emulator and does the builds needs a small kit installed: a of the right version, the command-line tools, and one Android system image. The gotcha that costs an afternoon is the emulator’s speed. On Linux it needs to borrow the real processor, which means the /dev/kvm device has to exist and your user has to be in the kvm group. Miss that and the emulator still starts, it just runs like it is wading through treacle, and you will assume you broke something.

your laptopSDK · JDKadbemulatormost worka real phonesensors · cameraa tester’s eyes
Two places to run it while you build. The bottle for most of the day; the real thing for anything the bottle cannot fake.

3 · A lifeboat you have to fit out

The easy way to run your JavaScript is : a free app from the store, you point it at your project, your code loads, no build step anywhere. It works right up until you need a feature it was not built with.

Expo Go carries a fixed kit of native features. The moment your app needs a that is not in that kit, and anything reading the phone’s own hardware is one, Expo Go simply cannot run it. What you do instead is build a : the same easy live-reloading sandbox, except fitted out with your native gear. You build it once, install it, and only build it again when the native parts of the project change, which is rarely. Day to day it behaves exactly like Expo Go.

Expo Gono build · fixed kitnotificationsfile pickerlocationhealth storethat one librarynot aboarddev clientbuild once · your kitnotificationsfile pickerlocationhealth storethat one libraryyours, lashed in
The stock lifeboat has whatever kit it shipped with. The dev client is the same boat with your gear lashed in.

4 · Loading the hold

Building that dev client, and later the real thing you hand to people, is the slow job. It packs the JavaScript, the native code and every asset into one . There are two places it can happen.

On your own machine: , invoked through the Expo command, a few minutes after the first slow run, and free forever. This needs the Android SDK sitting on the machine, which is the one real install cost of the whole setup. Or in the cloud, on , whose free tier covers about fifteen Android builds a month and needs nothing installed locally at all. Fifteen is plenty, because you are not rebuilding often. A build is loading the hold for a voyage, not something you do between edits.

The tooling itself is free. Local builds, the emulator, the bundler and the live reload cost nothing, and there is no charge for awesomeness; the only line item with a price tag is the cloud build service, which you can ignore entirely if you have the SDK installed.

sourceyour machine · Gradlefree · needs the SDKEAS cloudfree tier ~15/mo · nothing localone APK
Rarely, and only when the native parts change: the source becomes one installable file, on your machine or on someone else's.

5 · The tender run

With the dev client installed, the everyday loop has nothing slow in it. You run the Expo command and it starts on your laptop. Metro watches your files, and every time you save it repackages the JavaScript and hands the fresh bundle to whatever is running the dev client, over the local network. The emulator and a real phone on the same wifi can both be attached at once, and both update a second or so after the save. The edits a file, you watch the screen redraw. It is the tightest loop in the whole stack, and it never touches Gradle.

your laptopeditoragentMetroJS bundle on savesame wifiemulatorreloads in ~1sa real phonereloads in ~1s
The bundler is the launch that ferries fresh supplies out to whatever is floating. A save, then a second, then it is on the screen.

6 · Resupply at sea

At some point someone who is not you needs the app on their phone. The first delivery is a single APK: build it, send a link, they allow installs from that source once, and it is on. That is a , and it is fine for one person or a handful.

After that first install, JavaScript-only changes go out . You publish the new bundle to an update service, the app checks for it on launch and swaps it in, and your machine is not in that path at all. Twenty small fixes in an afternoon reach the tester without twenty reinstalls. When the group grows, the platform’s takes over: a one-time developer-account fee on Android, up to a hundred testers, no review wait, real auto-updates. Native changes are the exception the whole way through. Those need a fresh APK, because you have changed what is in the hold, not just the cargo on deck.

your laptoppublish updateupdate serviceholds the bundlechecks on launchnative change → a new APK instead
A ship already at sea gets a resupply run, not a return to port. Your machine is not on the line.

7 · The chart room does not change

Everything above is new. The layer over the top is not. bosun-x still holds the project’s spec, its task list and the note that lets the next session start without re-deriving a month of decisions, and it does not care in the slightest that this hull is a phone app instead of a web service. A dozen agent sessions on a mobile project scatter exactly like a dozen on anything else, and the fix is the same folder of plain files.

The one honest difference is the running half. bosun-x’s dashboard reads live container state to tell you what is up right now, and a phone app has no container on an always-on box to read. So that panel is quiet for this project. The spec, the tasks and the handoff carry the weight instead, which is where the real value was anyway.

bosun-xspectaskshandoffrunninga web appokokfreshupthe phone appokokfreshon a phonean old toolthinokstaleupone folder of Markdown + YAML · the agent reads and writes it
Same folder of Markdown and YAML, same dashboard. The 'what is running' column just has nothing to say about a hull that is out at sea.

8 · The whole yard

Put it together and it is one workshop and a set of lines running out of it. Your laptop holds the editor, the agent, the bundler and the SDK. The dry dock is an emulator and, when it matters, a real phone. A build turns the source into one APK, on your machine or in the cloud, and it happens rarely. The bundler carries every edit out to whatever is running, in about a second. Testers get one APK and then a stream of over-the-air updates. bosun-x sits across the top holding the spec and the log, same as for anything else. And the app, once it is built, runs on a phone and needs nothing of yours to be switched on.

bosun-x · the chart room · spec, tasks, handoffyour laptopeditor · agentMetroAndroid SDK · JDKthe dry dockemulatora phonebuild linesource↓ machine / EASone APKrarelybundle · every saveoften · over-the-air updatesthe app runs hereno box of yoursstays on
The finished yard for a phone app: a workshop, two places to run it, a build line that is used rarely, a resupply line that is used often, and the chart room over the top.

You do not stand all of this up on day one. A laptop, an agent, the SDK and an emulator gets you moving. You add a real phone the first time the emulator lies to you about how something feels. You reach for the cloud build the day the SDK install gets under your skin, and the tester track when a link and a sideload stop scaling. Every piece earns its place the same way it does in the web dockyard, by the specific ache it takes away.

What took me longest was none of the setup. It was dropping the web habit of asking where the thing runs, and getting comfortable with the answer being a phone I will never see, somewhere, doing fine without me.

-x