Skip to content

Commit 778382a

Browse files
mrousavyfacebook-github-bot
authored andcommitted
RNGP - Add option to disable bundle compression (was: Make all React Native apps start 12% faster) (#49449)
Summary: Okay the title is a bit clickbaity, but this is actually true. (on Android) We (Janic, Szymon, Ruby and Me) discovered something interesting. React Native uses `mmap` for mapping the JS bundle to RAM, to avoid having to load the entire thing instantly at app startup. Ruby doubted that this was true - so we investigated. Apparently on Android, resources are **compressed**. And if the JS bundle is stored compressed, it has to be uncompressed before it can be loaded into RAM, hence not allowing it to be mmapp'ed! (see [`_CompressedAsset::getBuffer`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/androidfw/Asset.cpp;l=903?q=Asset.cpp)) So with this PR, we now add `.bundle` files to `noCompress` in the react-native gradle plugin, which disables compression for the JS bundle. We discovered while improving the performance of one of our clients: **Discord**. In our tests, **this improved the TTI of the Discord app by 400ms!! (or 12%)** 🤯🚀 NOTE: Yes, the .apk will now be bigger. But; Google Play compresses it anyways, so the **download size** of your .apk will likely not increase by much. It will be bigger on disk though. ## Changelog: [ANDROID] [CHANGED] Add option to disable bundle compression to improve startup time <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests Pull Request resolved: #49449 Test Plan: ### 1. Verify compression is disabled Build two apps, one with this patch and one without. When I did this using the RN community template, the one without this patch was 47,6 MB, and the one with this patch was 48 MB in size. So the .apk got bigger, which is what we expected ### 2. Verify app startup is faster Use tools like react-native-performance or custom markers to measure TTI. In our tests, we shaved off 400ms from the startup time, which was about 12% of Discord's total TTI. (on a low-end Android device) In Expensify, we improved the TTI by 14-20% with this change (source: Expensify/App#56930) Reviewed By: javache, cipolleschi Differential Revision: D69742221 Pulled By: cortinico fbshipit-source-id: bd59d77662bd30a3acdbb2e9f8d8f23db922c3f2
1 parent 41b597c commit 778382a

File tree

2 files changed

+21
-0
lines changed

2 files changed

+21
-0
lines changed

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactExtension.kt

+10
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ abstract class ReactExtension @Inject constructor(val project: Project) {
7676
val bundleAssetName: Property<String> =
7777
objects.property(String::class.java).convention("index.android.bundle")
7878

79+
/**
80+
* Whether the Bundle Asset should be compressed when packaged into a `.apk`, or not. Disabling
81+
* compression for the `.bundle` allows it to be directly memory-mapped to RAM, hence improving
82+
* startup time - at the cost of a larger resulting `.apk` size.
83+
*
84+
* Default: false
85+
*/
86+
val enableBundleCompression: Property<Boolean> =
87+
objects.property(Boolean::class.java).convention(false)
88+
7989
/**
8090
* Toggles the .so Cleanup step. If enabled, we will clean up all the unnecessary files before the
8191
* bundle task. If disabled, the developers will have to manually cleanup the files. Default: true

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt

+11
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class ReactPlugin : Plugin<Project> {
8181
}
8282
configureAutolinking(project, extension)
8383
configureCodegen(project, extension, rootExtension, isLibrary = false)
84+
configureResources(project, extension)
8485
}
8586

8687
// Library Only Configuration
@@ -110,6 +111,16 @@ class ReactPlugin : Plugin<Project> {
110111
}
111112
}
112113

114+
/** This function configures Android resources - in this case just the bundle */
115+
private fun configureResources(project: Project, reactExtension: ReactExtension) {
116+
project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
117+
val bundleFileExtension = reactExtension.bundleAssetName.get().substringAfterLast('.', "")
118+
if (!reactExtension.enableBundleCompression.get() && bundleFileExtension.isNotBlank()) {
119+
ext.androidResources.noCompress.add(bundleFileExtension)
120+
}
121+
}
122+
}
123+
113124
/** This function sets up `react-native-codegen` in our Gradle plugin. */
114125
@Suppress("UnstableApiUsage")
115126
private fun configureCodegen(

0 commit comments

Comments
 (0)