|
7 | 7 | _{author}, {revdate}_
|
8 | 8 |
|
9 | 9 |
|
10 |
| -Until recently, Gradle was the only realistic option for Android builds. Today, Mill - a fast, predictable build tool for Java, Scala, and Kotlin - can build Android applications end-to-end. |
| 10 | +Until recently, Gradle was the only realistic option for Android builds. Today, Mill provides an alternative to Gradle that is easier to use and learn. |
11 | 11 |
|
12 |
| -In less than a year, Mill went from minimal Android support to producing installable APKs for projects as complex as xref:mill::android/hilt-sample.adoc[the Android Architecture Samples] and https://github.com/vaslabs/Pokedex_Compose_Multi_Module/tree/testing-mill[multi-module apps] with xref:mill::android/java.adoc#_using_third_party_native_libraries[native code] and xref:mill::android/compose-samples.adoc[Jetpack Compose]. |
| 12 | +In less than a year, Mill went from minimal Android support to producing installable APKs for projects as complex as: |
| 13 | + |
| 14 | +- xref:mill::android/hilt-sample.adoc[Jetpack compose + Dependency Injection with Hilt] |
| 15 | +- xref:mill::android/java.adoc#_using_third_party_native_libraries[Android Native] |
| 16 | +- xref:mill::android/compose-samples.adoc[Jetpack Compose]. |
13 | 17 | // end::header[]
|
14 |
| -Multi-module Pokedex sample app (https://github.com/NicosNicolaou16/Pokedex_Compose_Multi_Module[original], https://github.com/vaslabs/Pokedex_Compose_Multi_Module/tree/testing-mill[with mill]) |
| 18 | +
|
| 19 | +- Multi-module Pokedex sample app (https://github.com/NicosNicolaou16/Pokedex_Compose_Multi_Module[original], https://github.com/vaslabs/Pokedex_Compose_Multi_Module/tree/testing-mill[with mill]) |
15 | 20 | image:AndroidPokedexMultimoduleExample.png[A multi-module Android app built with Mill, showing a list of Pokémon and details for each pokemon.]
|
16 | 21 |
|
17 |
| -Unlike Gradle, where build steps are buried inside plugins and DSL magic, Mill exposes each Android step as a task you can run, override, or extend. |
18 |
| -Want to see the merged manifest? Run |
19 |
| -[,console] |
20 |
| ----- |
21 |
| -./mill app.androidMergedManifest |
22 |
| ----- |
23 | 22 |
|
24 |
| -Curious what rules are passed to R8? Run |
| 23 | +=== Why you might prefer this to Gradle |
| 24 | + |
| 25 | +Because Mill’s Android support is built out of simple, object-oriented modules (AndroidModule, AndroidAppModule, etc.), the entire pipeline is transparent and hackable. If something doesn’t work, you don’t need to wait for a plugin update — you can open the task in your IDE, see the source, and tweak it yourself. This is the same design that let us implement end-to-end Android support in under a year, and it’s what makes Mill attractive if you value control and debuggability in your build. |
| 26 | + |
| 27 | +For instance, you can inspect Mill's android packaging process with: |
| 28 | + |
25 | 29 | [,console]
|
26 | 30 | ----
|
27 |
| -./mill app.androidProguard |
28 |
| ----- |
| 31 | +$ ./mill __.androidApk |
| 32 | +[1/1] inspect |
| 33 | +app.androidApk(AndroidAppModule.scala:439) |
| 34 | + Signs the APK using a keystore to generate a final, distributable APK. |
29 | 35 |
|
30 |
| -Because Mill’s Android support is built out of simple, object-oriented modules (AndroidModule, AndroidAppModule, etc.), the entire pipeline is transparent and hackable. If something doesn’t work, you don’t need to wait for a plugin update — you can open the task in your IDE, see the source, and tweak it yourself. This is the same design that let us implement end-to-end Android support in under a year, and it’s what makes Mill attractive if you value control and debuggability in your build. |
| 36 | + The signing step is mandatory to distribute Android applications. It adds a cryptographic |
| 37 | + signature to the APK, verifying its authenticity. This method uses the `apksigner` tool |
| 38 | + along with a keystore file to sign the APK. |
31 | 39 |
|
32 |
| -This post is a walk through of the basic Android build flow in Mill: what it does, why it’s complex, and why Mill makes it easier to reason about. |
| 40 | + If no keystore is available, a new one is generated using the `keytool` utility. |
| 41 | +
|
| 42 | + For more details on the apksigner tool, refer to: |
| 43 | + [[https://developer.android.com/tools/apksigner apksigner Documentation]] |
| 44 | +
|
| 45 | +Inputs: |
| 46 | + androidSdkModule0.apksignerPath |
| 47 | + app.androidAlignedUnsignedApk |
| 48 | + app.androidSignKeyDetails |
| 49 | +... |
| 50 | +---- |
33 | 51 |
|
| 52 | +This post is a walk through of the basic Android build flow in Mill: what it does, why it’s complex, and why Mill makes it easier to reason about. |
34 | 53 |
|
35 | 54 | == The Android build process
|
36 | 55 |
|
@@ -208,15 +227,81 @@ Start the emulator and run the app
|
208 | 227 | ./mill show app.androidRun --activity com.example.android.architecture.blueprints.todoapp.TodoActivity
|
209 | 228 | ----
|
210 | 229 |
|
| 230 | +The Android Todo App built with Mill |
| 231 | +image:AndroidTodoExample.png[The Todo app built with Mill, showing a list of tasks and a button to add new tasks.] |
| 232 | + |
211 | 233 | Run the instrumented tests and watch the app being tested inside the emulator:
|
212 | 234 |
|
213 | 235 | [source,bash]
|
214 | 236 | ----
|
215 | 237 | ./mill app.androidTest
|
216 | 238 | ----
|
217 | 239 |
|
218 |
| -The Android Todo App built with Mill |
219 |
| -image:AndroidTodoExample.png[The Todo app built with Mill, showing a list of tasks and a button to add new tasks.] |
| 240 | +Let's say you want to know how the apk is built. First, you can check the plan of `androidApk`, i.e which |
| 241 | +tasks it depends on: |
| 242 | +[,console] |
| 243 | +---- |
| 244 | +$ ./mill plan app.androidApk |
| 245 | +[1/1] plan |
| 246 | +androidSdkModule0.sdkPath |
| 247 | +androidSdkModule0.buildToolsVersion |
| 248 | +androidSdkModule0.platformsVersion |
| 249 | +androidSdkModule0.remoteReposInfo |
| 250 | +androidSdkModule0.installAndroidSdkComponents |
| 251 | +androidSdkModule0.buildToolsPath |
| 252 | +androidSdkModule0.apksignerPath |
| 253 | +androidSdkModule0.zipalignPath |
| 254 | +app.mandatoryMvnDeps.super.javalib.JavaModule |
| 255 | +app.kotlinVersion |
| 256 | +---- |
| 257 | + |
| 258 | + |
| 259 | +You can use this to visualise the relationships between these tasks and how they feed each other and ultimately the `androidApk` task: |
| 260 | + |
| 261 | +[,console] |
| 262 | +---- |
| 263 | +$ ./mill visualizePlan app.androidApk |
| 264 | +[3/3] visualizePlan |
| 265 | +[ |
| 266 | + ".../architecture-samples/out/visualizePlan.dest/out.dot", |
| 267 | + ".../architecture-samples/out/visualizePlan.dest/out.json", |
| 268 | + ".../architecture-samples/out/visualizePlan.dest/out.png", |
| 269 | + ".../architecture-samples/out/visualizePlan.dest/out.svg", |
| 270 | + ".../architecture-samples/out/visualizePlan.dest/out.txt" |
| 271 | +] |
| 272 | +[3/3] ============================== visualizePlan app.androidApk ============================== 2s |
| 273 | +---- |
| 274 | + |
| 275 | +You can also check the code of each task and what it does exactly inside your IDE: |
| 276 | +image:AndroidIDEExplore.png[Exploring the Mill Android build tasks in an IDE, showing the source code for the androidApk task.] |
| 277 | + |
| 278 | + |
| 279 | +In addition, due to xref:12-direct-style-build-tool.adoc#_direct_style_builds[Mill's direct style], you can reason what's going on with relative ease. |
| 280 | + |
| 281 | +=== Example: tweak the build in your `build.mill` |
| 282 | +[source,scala] |
| 283 | +---- |
| 284 | +import mill._ |
| 285 | +import mill.androidlib._ |
| 286 | +
|
| 287 | +object app extends AndroidAppModule { |
| 288 | + def androidApplicationNamespace = "com.example.app" |
| 289 | + def androidApplicationId = "com.example.app" |
| 290 | + def androidCompileSdk = 35 |
| 291 | +
|
| 292 | + // Add extra files into the APK |
| 293 | + override def androidPackageableExtraFiles = super.androidPackageableExtraFiles() |
| 294 | + Seq( |
| 295 | + AndroidPackageableExtraFile( |
| 296 | + PathRef(moduleDir / "assets/about.txt"), |
| 297 | + os.RelPath("assets/about.txt") |
| 298 | + ) |
| 299 | + ) |
| 300 | +
|
| 301 | +} |
| 302 | +---- |
| 303 | + |
| 304 | +=== Further Exploration |
220 | 305 |
|
221 | 306 | You may also inspect xref:mill::android/android-initial-setup.adoc[the getting started docs] to find out more.
|
222 | 307 |
|
|
0 commit comments