|
| 1 | +--- |
| 2 | +title: 'Compiled releases: Android' |
| 3 | +description: Requirements, signing, threading annotations, R8 and mapping crashes back to TypeScript in a compiled Android release. |
| 4 | +contributors: |
| 5 | + - NathanWalker |
| 6 | +--- |
| 7 | + |
| 8 | +## Requirements |
| 9 | + |
| 10 | +- **JDK 17** or newer and the **Android SDK**. The compiler ships its own Gradle wrapper. |
| 11 | +- macOS or Linux. Windows is not supported yet. |
| 12 | +- The app runs on **Android 7 (API 24)** and later. |
| 13 | + |
| 14 | +## Build, sign, run |
| 15 | + |
| 16 | +A compiled build is a release build, and Android signs every release, so all four `--key-store-*` options are |
| 17 | +required: |
| 18 | + |
| 19 | +```bash |
| 20 | +ns build android --compiled --key-store-path release.keystore --key-store-password … \ |
| 21 | + --key-store-alias … --key-store-alias-password … # signed .apk; add --aab for a bundle |
| 22 | +ns run android --compiled --key-store-path … # build, install and launch |
| 23 | +``` |
| 24 | + |
| 25 | +They sign as Android Studio does. To try a build before you have your upload key, make a throwaway keystore (see |
| 26 | +[Getting started](/guide/publishing/compiled-getting-started#_2-build-and-run)). |
| 27 | + |
| 28 | +Android installs an app over another only when both are signed with the same key. Moving between a debug build |
| 29 | +(signed with the SDK's debug key) and a release signed with another key fails with "a previously installed package |
| 30 | +of the same name has a different signature": uninstall first (`adb uninstall <app id>`). |
| 31 | + |
| 32 | +## The Gradle project |
| 33 | + |
| 34 | +`platforms/compiled/android` is a Gradle project, regenerated on every build. App_Resources carry over as the CLI |
| 35 | +carries them: `res/`, `AndroidManifest.xml` (merged with plugins'), `app.gradle` and `before-plugins.gradle`, and |
| 36 | +fonts. Plugins' Java, Kotlin and Gradle dependencies are linked unchanged. |
| 37 | + |
| 38 | +The kit marks main-thread code `@UiThread` and code that may run on any thread `@AnyThread`, and Android lint |
| 39 | +checks calls against them. |
| 40 | + |
| 41 | +`fetch` and `XMLHttpRequest` are not available on Android yet. They are on iOS. |
| 42 | + |
| 43 | +## Minification (R8) |
| 44 | + |
| 45 | +Members reached by name at run time (a method called on an `any` value, `typeof x.getFoo`, `'getFoo' in x`) are |
| 46 | +kept from R8's renaming by a scan of the compiled code. A name computed at run time (`x[name]()` with `name` built |
| 47 | +from strings) isn't found by the scan: keep such members with a ProGuard rule in |
| 48 | +`App_Resources/Android/app.gradle`, and check the minified release on a device. |
| 49 | + |
| 50 | +By default every class and member of the kit is kept. `android: { release: { narrowKitKeeps: true } }` keeps only |
| 51 | +what the kit's reflection needs and lets R8 remove the rest: on Recipes (Vue) the kit's dex goes from 3.3 to 2.0 MB |
| 52 | +and the APK from 2.44 to 1.92 MB. It is opt-in until it has been checked on larger apps; under it, a kit member |
| 53 | +reached only through a computed name is removed. |
| 54 | + |
| 55 | +## Crashes and debugging |
| 56 | + |
| 57 | +Kotlin has no source-location directive, so the build writes a line table (`source-lines.json`) beside the Gradle |
| 58 | +project. `ns-native-retrace` reads a stack trace in your TypeScript lines: it runs R8's retrace with the build's |
| 59 | +`mapping.txt`, then maps your app's frames to your files and the kit's frames to `@nativescript/core` files. |
| 60 | + |
| 61 | +```bash |
| 62 | +adb logcat -d -s AndroidRuntime | npx ns-native-retrace platforms/compiled/android |
| 63 | +npx ns-native-retrace platforms/compiled/android crash.txt # a trace saved from your crash reporter |
| 64 | +``` |
| 65 | + |
| 66 | +Keep `mapping.txt` and `source-lines.json` with each release you upload. When the Kotlin compile fails, `ns build` |
| 67 | +prints each error at the TypeScript line it came from. |
| 68 | + |
| 69 | +`error.stack` lists native frames, such as `class.method(File.kt:line)`, which `ns-native-retrace` maps. Uncaught |
| 70 | +errors go to `Application.uncaughtErrorEvent` and are logged as `***** Fatal JavaScript exception *****` (logcat |
| 71 | +tag `JS`). Debug a compiled release in Android Studio, as a native app. |
| 72 | + |
| 73 | +## Build times |
| 74 | + |
| 75 | +Measured on a blank app with R8, 2026-10-13: the first build takes a minute or two, a one-line change about 45 s. |
0 commit comments