From 0a279c0a2fe3221f05b0e662302c5f865b96c91d Mon Sep 17 00:00:00 2001 From: Martin Konicek Date: Wed, 6 Jan 2016 19:44:00 +0000 Subject: [PATCH] [Android] Add support for split build per architecture This allows everyone to deploy significantly smaller APKs by building separate APKs for ARM, x86 architectures. For a simple app, Both ARM, x86 APKs are about 4MB which is about 50% reduction compared to the universal APK. Test Plan: Created a sample project, uncommented `// include "armeabi-v7a", 'x86'`. cd android ./gradlew assembleDebug Three APKs were created, unzipped each: one has only x86 binaries, one has ARM binaries, one has both. ./gradlew assembleRelease Three APKs were created, JS bundle is correcly added to assets. react-native run-android The correct APK is installed on the emulator and the app runs (Gradle output: "Installing APK 'app-x86-debug.apk'"). With the line commented out the behavior is exactly the same as before, only one universal APK is built. Checked that version codes are set correctly as decribed in http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits http://developer.android.com/intl/ru/google/play/publishing/multiple-apks.html aapt l -a ../android/app/build/outputs/apk/app-armeabi-v7a-debug.apk | grep android:version A: android:versionCode(0x0101021b)=(type 0x10)0x100001 A: android:versionName(0x0101021c)="1.0" (Raw: "1.0") aapt l -a ../android/app/build/outputs/apk/app-x86-debug.apk | grep android:version A: android:versionCode(0x0101021b)=(type 0x10)0x200001 A: android:versionName(0x0101021c)="1.0" (Raw: "1.0") aapt l -a ../android/app/build/outputs/apk/app-universal-debug.apk | grep android:version A: android:versionCode(0x0101021b)=(type 0x10)0x1 A: android:versionName(0x0101021c)="1.0" (Raw: "1.0") --- .../templates/src/app/build.gradle | 41 ++++++++++++++++++- .../templates/src/app/react.gradle | 23 ++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/local-cli/generator-android/templates/src/app/build.gradle b/local-cli/generator-android/templates/src/app/build.gradle index 1298bb3f474c0c..b68fc50c515e05 100644 --- a/local-cli/generator-android/templates/src/app/build.gradle +++ b/local-cli/generator-android/templates/src/app/build.gradle @@ -1,5 +1,7 @@ apply plugin: "com.android.application" +import com.android.build.OutputFile + /** * The react.gradle file registers two tasks: bundleDebugJsAndAssets and bundleReleaseJsAndAssets. * These basically call `react-native bundle` with the correct arguments during the Android build @@ -49,6 +51,22 @@ apply plugin: "com.android.application" apply from: "react.gradle" +/** + * Set this to true to create three separate APKs instead of one: + * - A universal APK that works on all devices + * - An APK that only works on ARM devices + * - An APK that only works on x86 devices + * The advantage is the size of the APK is reduced by about 4MB. + * Upload all the APKs to the Play Store and people will download + * the correct one based on the CPU architecture of their device. + */ +def enableSeparateBuildPerCPUArchitecture = false + +/** + * Run Proguard to shrink the Java bytecode in release builds. + */ +def enableProguardInReleaseBuilds = true + android { compileSdkVersion 23 buildToolsVersion "23.0.1" @@ -63,12 +81,33 @@ android { abiFilters "armeabi-v7a", "x86" } } + splits { + abi { + enable enableSeparateBuildPerCPUArchitecture + universalApk true + reset() + include "armeabi-v7a", "x86" + } + } buildTypes { release { - minifyEnabled false // Set this to true to enable Proguard + minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits + def versionCodes = ["armeabi-v7a":1, "x86":2] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + } + } + } } dependencies { diff --git a/local-cli/generator-android/templates/src/app/react.gradle b/local-cli/generator-android/templates/src/app/react.gradle index 1e08b00f13b9e6..f2152d1f68480c 100644 --- a/local-cli/generator-android/templates/src/app/react.gradle +++ b/local-cli/generator-android/templates/src/app/react.gradle @@ -74,14 +74,33 @@ task bundleReleaseJsAndAssets(type: Exec) { enabled config.bundleInRelease ?: true } +void runBefore(String dependentTaskName, Task task) { + Task dependentTask = tasks.findByPath(dependentTaskName); + if (dependentTask != null) { + dependentTask.dependsOn task + } +} + gradle.projectsEvaluated { + // hook bundleDebugJsAndAssets into the android build process + bundleDebugJsAndAssets.dependsOn mergeDebugResources bundleDebugJsAndAssets.dependsOn mergeDebugAssets - processDebugResources.dependsOn bundleDebugJsAndAssets + + runBefore('processArmeabi-v7aDebugResources', bundleDebugJsAndAssets) + runBefore('processX86DebugResources', bundleDebugJsAndAssets) + runBefore('processUniversalDebugResources', bundleDebugJsAndAssets) + runBefore('processDebugResources', bundleDebugJsAndAssets) // hook bundleReleaseJsAndAssets into the android build process + bundleReleaseJsAndAssets.dependsOn mergeReleaseResources bundleReleaseJsAndAssets.dependsOn mergeReleaseAssets - processReleaseResources.dependsOn bundleReleaseJsAndAssets + + runBefore('processArmeabi-v7aReleaseResources', bundleReleaseJsAndAssets) + runBefore('processX86ReleaseResources', bundleReleaseJsAndAssets) + runBefore('processUniversalReleaseResources', bundleReleaseJsAndAssets) + runBefore('processReleaseResources', bundleReleaseJsAndAssets) + }