Skip to content

Android Native C++ Libraries Build #423

Closed
@cortinico

Description

@cortinico

Introduction

The goal of this issue to bring awareness of what is the current status of the Android native builds, express some of the improvements we shipped, and collect feedback on some of the potential direction we might take.

With the new architecture, and specifically with the TurboModule capability (see #40) we expect more users having to deal with C++ code, therefore we believe it's important to bring clarity on this topic.

Details

Currently the react-native project needs to build both Java/Kotlin (app layer) code and C++ (native layer) in order to be shipped and be used by Android developers.
Whenever we release a new version of react-native we:

  1. Build all the app layer code using the Java/Kotlin compilers
  2. Build all the native code using the Android NDK (specifically we use ndk-build).
  3. Bundle all the dynamic libraries (aka the .so files) together with the .class files and so on inside an Android Archive (aka a .aar file) and we place it inside the android/com/facebook/react/react-native/<version> folder of our NPM module.

This will make possible to build a React Native project with Gradle using the aforementioned folder as a Maven Repository (see here).

As long as you don't need to compile C++ code, you will be good with the current setup. If you need to build C++ code instead (say because you want to add a TurboModule or you're developing a react-native libraries that relies on native code), you need to setup a native build with the Android NDK.

Sadly, setting up an Android native build is not as easy as we would like it to be. Here are some of the friction points and potential improvements.

Discussion points

AGP Android NDK Apis

What: The ReactAndroid project is (was) not using the AGP NDK Apis
Who is affected: react-native contributors that are building the Android project.
Status: ✅ Shipped

Historically, the react-native project was using a custom Gradle task to invoke the Android NDK (see here) rather than AGP's API externalNativeBuild.

This had a variety of implications, namely:

  • AGP was not aware of us running a native build (i.e. we would have to take care of the cleanups).
  • There were a number of cache misses on the Gradle side (i.e. some Gradle tasks were making extensive use of doLast and would have been re-executed at every builds).
  • We could not specify variant specific flags for the native build.

Some of the complexity here was that the ReactAndroid native build relies on several third party libraries that should be downloaded and prepared afterwards. We cleaned up the ReactAndroid build with the following PRs that are aimed at extracting those Gradle tasks to separate classes and fix the cache misses:

The switch to the NDK Apis was done in this PR:

If you are a react-native contributor, please note the implications on the build time (and how to reduce it if its too long for you) in the commit message of #32443

Cmake Support

What: The react-native Android ecosystem is relying on ndk-build.
Who is affected: Any developer which is building a module with native code (e.g. a TurboModule or similar).
Status: 💬 Open for discussion ✅ Shipped in RN 0.70

Currently all the C++ code for Android is built using ndk-build. Google is anyway advertising to do not use ndk-build for new project but prefer Cmake instead (see here).

We haven't yet evaluated what would be the impact of migrating the builds to Cmake and we're happy to collect feedback from the community.

Prefab Support

What: The react-native .aar is not exposing headers/dynamic libraries and is forcing consumers to rebuild everything from source.
Who is affected: Any developer which is building a module with native code (e.g. a TurboModule or similar).
Status: 💬 Open for discussion ✅ Shipped in RN 0.71

AGP 4.1 added the support for publishing prefabs inside an .aar (see here). We're not using prefab in any from at this stage:

Consuming Prefabs

Some of the native libraries we depend on are offering prefab support, but we're not actively using it. An example is fbjni - see facebookincubator/fbjni#35. We're instead relying on a custom Gradle machinery to extract headers and .sos from the fbjni tarball: facebook/react-native#32426

Publishing Prefabs

Publishing prefabs inside the react-native .aar was blocked by the AGP Android NDK Apis task.

We should investigate what would be the impact of shipping all/some of the native dependencies that we're bundling inside the react-native .aar. Specifically users that are adding a TurboModule will need to have the following .so files + related headers: libjsi, libfbjni, libglog, libfolly_json, libyoga, libreact_nativemodule_core, libturbomodulejsijni, librrc_view, libreact_render_core, libreact_render_graphics, libreact_codegen_rncore.

The impact of this in terms of bundle size and build time hasn't been assessed yet.

Specifically if you're a react-native library author you might benefit from depending on react-native as you won't need to fetch and build all the shared libraries (such as Glog, Folly and so on). This has however implications in terms of ABI stability that we haven't yet fully assessed.

Build from source vs build from artifacts.

There has been some discussions and confusions in the community between prefabs and the build from source v. build from artifacts. We would like to clarify that we believe in:

  • Sticking ReactAndroid builds to build from source, with all the dependencies code available locally. The Consuming Prefabs point presented earlier should be considered as an improvement to speedup built time and will anyway be an opt-in feature (i.e. you will still be able to build everything from source).
  • Delivering the best build experiences to our consumer. Currently react-native is distributed as prebuilt (an .aar) and, as we evolve our framework/architecture, we will try to reduce the build impact on consumers as much as possible while still allowing the flexibility to build from source to some extent (see this wiki page).

Variant Aware Packaging

What: The react-native .aar needs to be manually patched to remove the unnecessary dynamic libaries.
Who is affected: All the react-native users to some extent.
Status: 💬 Open for discussion ✅ Shipped in RN 0.71

We currently rely on the enableVmCleanup flag (enabled by default) to run a cleanup of all the .so files that we suppose they won't be needed in the final APK. For instance if you're building a Debug build using Hermes, you will not need libhermes-executor-debug.so, and so on.

This has to be performed manually with a FileTree.visit during the build which is really error prone. Some alternatives to this approach could be:

  • Investigate if this can be moved to use AGP 7's new Artifact API
  • Release variant-aware version of the react-native .aar so that they will be matched accordingly to the app build (e.g. a react-native-debug.aar and so on).

The impact of this hasn't been assessed yet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    👓 TransparencyThis label identifies a subject on which the core has been already discussing prior to the repo

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions