Building React Native Apps page

Learn how to build Android App Bundle (AAB) files.

Overview

In this section, you will:

  • Learn about the differences between APK and AAB files.
  • Build an AAB file.

Objective 1: Create an Android App Bundle (AAB)

When developing Android applications, understanding the different formats used for packaging and distributing your app is crucial. Two primary formats you’ll encounter are the Android App Bundle (AAB) and the Android Package (APK).

Each serves distinct purposes and offers unique benefits. Let’s dive into what these formats are and why they matter.

What is an APK?

An Android Package (APK) is the traditional file format used to distribute and install applications on Android devices. Essentially, an APK is a compressed archive that contains all the necessary components for an app to run, including the compiled code, resources, assets, and manifest file. When you download an app from the Google Play Store or another source, it typically comes in the form of an APK.

Key features of an APK:

  • Direct installation: APK files can be installed directly onto Android devices, either through the Play Store or by sideloading.
  • Universal packaging: An APK contains all resources and code required for every possible device configuration, such as different screen sizes, densities, and CPU architectures.
  • Larger file size: Because it includes resources for all configurations, an APK file can be quite large and may include unnecessary data for a particular device.

What is an AAB?

An Android App Bundle (AAB) is a more modern and efficient format introduced by Google. Unlike an APK, an AAB is not a final package that can be installed on a device. Instead, it contains all the compiled code and resources of an app, but in a way that allows Google Play to generate optimized APKs specifically tailored for each device configuration.

Key features of an AAB:

  • Optimized distribution: When you upload an AAB to the Google Play Store, the Play Store automatically generates and serves optimized APKs for each user’s device. This means the downloaded app will only contain the resources needed for that specific device, resulting in smaller download sizes and improved performance.
  • Mandatory for Play Store: Starting in 2021, Google Play requires new apps to be published using the AAB format.
  • Efficient updates: By using AAB, incremental updates can be more efficient as only the necessary changes are downloaded, reducing the update size.

Building an AAB

AAB files can be built by using this command provided by React Native:

react-native build-android --mode=release

The AAB will be in this location:

android/app/build/outputs/bundle/release/app-release.aab

You are able to create an AAB with or without signing it. However, if you want to upload the AAB to the Google Play store, you must sign it using an upload key keystore.

Setup 1

✏️ Update package.json to be:

{
  "name": "PlaceMyOrder",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "android:build": "react-native build-android --mode=release",
    "android:linkLocalhost": "adb reverse tcp:7070 tcp:7070",
    "api": "npm run android:linkLocalhost && place-my-order-api --port 7070",
    "clean": "rm -rf tsconfig.tsbuildinfo coverage android/.gradle android/build android/app/build node_modules/.cache",
    "depcheck": "depcheck .",
    "devtools": "react-devtools",
    "eslint": "eslint .",
    "eslint:fix": "eslint --fix .",
    "ios": "react-native run-ios",
    "lint": "npm run eslint && npm run prettier",
    "lint:fix": "npm run eslint:fix && npm run prettier:fix",
    "precheck": "npm run typecheck && npm run lint && npm run depcheck && npm test",
    "prettier": "prettier --check .",
    "prettier:fix": "prettier --write .",
    "start": "react-native start --reset-cache --experimental-debugger",
    "test": "jest",
    "test:inspect": "node --inspect-brk ./node_modules/.bin/jest --watch",
    "test:watch": "jest --watch",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@react-native-async-storage/async-storage": "^1.23.1",
    "@react-native-community/netinfo": "^11.3.2",
    "@react-native-google-signin/google-signin": "^11.0.1",
    "@react-navigation/bottom-tabs": "^6.5.20",
    "@react-navigation/native": "^6.1.17",
    "@react-navigation/stack": "^6.3.29",
    "react": "18.2.0",
    "react-native": "0.74.1",
    "react-native-gesture-handler": "^2.16.2",
    "react-native-maps": "^1.15.1",
    "react-native-safe-area-context": "^4.10.1",
    "react-native-screens": "^3.31.1",
    "react-native-vector-icons": "^10.1.0"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@babel/runtime": "^7.20.0",
    "@bitovi/eslint-config": "^1.8.0",
    "@react-native/babel-preset": "0.74.83",
    "@react-native/eslint-config": "0.74.83",
    "@react-native/metro-config": "0.74.83",
    "@react-native/typescript-config": "0.74.83",
    "@testing-library/react-native": "^12.5.0",
    "@types/jest": "^29.5.12",
    "@types/react": "^18.2.6",
    "@types/react-native-vector-icons": "^6.4.18",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "depcheck": "^1.4.7",
    "eslint": "^8.19.0",
    "identity-obj-proxy": "^3.0.0",
    "jest": "^29.6.3",
    "place-my-order-api": "^1.7.0",
    "prettier": "2.8.8",
    "react-devtools": "^5.2.0",
    "react-native-dotenv": "^3.4.11",
    "react-test-renderer": "18.2.0",
    "typescript": "5.0.4"
  },
  "engines": {
    "node": ">=20"
  }
}

Verify 1

The build logs will look something like this (but much, much longer):

npm run android:build

> PlaceMyOrder@0.0.1 android:build
> react-native build-android --mode=release

info Building the app...

> Task :app:createBundleReleaseJsAndAssets
debug Reading Metro config from /Users/bitovi/PlaceMyOrder/metro.config.js
warning: the transform cache was reset.
                Welcome to Metro v0.80.9
              Fast - Scalable - Integrated

# Many lines later…

> Task :react-native-gesture-handler:compileReleaseKotlin
w: file:///Users/bitovi/PlaceMyOrder/node_modules/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt:69:42 'constructor ReactModuleInfo(String!, String!, Boolean, Boolean, Boolean, Boolean, Boolean)' is deprecated. Deprecated in Java
w: file:///Users/bitovi/PlaceMyOrder/node_modules/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/FlingGestureHandler.kt:25:26 Parameter 'event' is never used

> Task :react-native-screens:compileReleaseJavaWithJavac
Note: /Users/bitovi/PlaceMyOrder/node_modules/react-native-screens/android/src/paper/java/com/swmansion/rnscreens/NativeScreensModuleSpec.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

BUILD SUCCESSFUL in 30s
216 actionable tasks: 211 executed, 5 up-to-date

Exercise 1

✏️ Run the script to generate the AAB file:

npm run android:build

Solution 1

After the build runs successfully, you can find the AAB in your project:

android/app/build/outputs/bundle/release/app-release.aab

Since the aab file is really a zip in disguise, change the filename to app-release.zip and open it. What can you find?

Next steps

Next, we will learn about Publishing and Updating.