Optimizing your Android Build for React Native
Have you seen the call to action in the Google Play Console? The one that lets you know that you should be using the bundle (.aab) format, instead of the APK. Are you familiar with the Hermes Engine for Android that came out with the release of React Native 0.61? I will share some tips on how to get both of these things working in your React Native project to help optimize your Android builds.
Recently I got this message with a note stating I could reduce the file size by an estimated 60.1% and I decided I should dig back into the Android bundle after the missteps I previously had with them and React Native.
So what were those missteps I experienced previously? After attending Chain React in July of 2019, I was stoked at the announcement of the Hermes engine to be released with React Native 0.61. Facebook followed up with a blog post about this announcement and how you could take advantage; I was sold and decided I was ready to dig in. While we were at it, I decided we should work on figuring out those Android Bundles. At the time, the project I was working on was about a 50MB APK.
After enabling Hermes and creating my first bundle everything seemed to be working great locally! We uploaded successfully to the Play Console with much anticipation with our significantly reduced file size, sent to our users in our Alpha test group and experienced our first letdown: the app would NOT load. The users were able to successfully download the app from the PlayStore but it would never get beyond a white splash screen, just hanging out there with no feedback. Such a disappointment. We weren’t sure if this was to do with the bundle or Hermes at the moment.
Of course, we were mid-release cycle without a lot of time to explore what was going on. I attempted to figure out the bundle but struggled at the time with how to test that .aab
file without uploading to the Play Console to discover it to still not be working.
Fast forward about 6 months, seeing that Call To Action I had a conversation with my manager and decided it was time to figure out this bundling. I was not going to let it hold me back again. It’s amazing how you can come back to a problem with a fresh set of eyes, and of course months of additional experience under my belt, to knock it out of the park.
Here is what I learned and how you can take advantage of the Hermes engine, Android bundle, and test and verify that bundle locally!
The first step is to enable Hermes. This is a pretty simple change, toggle the boolean configuration in the app/build.gradle; however, you MUST clean and rebuild the app or it won’t take effect due to caching. This can be accomplished with the gradlew clean command. I have created a convenience script in my package.json
in order to help facilitate this.
Here is my build.gradle file along with the package.json snippet.
After you run the clean you can run a build that has now enabled Hermes!
Next up is the Android bundle. Again, this is a pretty simple change in how you create that Release variant for your React Native builds. In the past, we have used gradlew assembleRelease
which generated our APK file that we uploaded to the Play Console. In order to bundle you simply change that command to be gradlew bundleRelease
this will then generate that bundle .aab
file. Where it fell apart for me in the past was then deconstructing that bundle back to an APK that I could install to a device or emulator and validate. Enter the bundletool from Android, although I felt like the documentation was a bit lacking in its actual usage. This time I was prepared to settle in for the long haul and figure it out. Previously I had followed the tip to download the bundletool which sent me to the GitHub repo and a list of jar files. Of course, I downloaded the file and then I will admit, I got a bit lost in how to use it.
This attempt I figured there had to be a way to install bundletool in which I could call it locally with a PATH variable defined. A quick Google search led me down the path of a Homebrew package for bundletool. A simple brew install bundletool
and I was on my way!
Well, then what? Again not great documentation that I could find on how to take my bundle and create the APK I could install locally. Eventually, I settled on a couple of commands I could use to do just this. I have my bundle command, generate APK command which takes the bundle and creates that associated APK, followed by install APK which will install to a connected device or emulator. Here is what has worked for me:
"scripts": {
"android:bundle": "cd android && ./gradlew app:bundleRelease && cd ..","android:gen-apks": "bundletool build-apks --bundle=./android/app/build/outputs/bundle/release/app.aab --output=./bundle/myapp.apks --ks=./android/app/android.keystore --ks-key-alias=homehero-release","android:install-apks": "bundletool install-apks --apks=./bundle/myapp.apks",
}
Perfect, I was now able to verify my bundle locally. I discovered I still had the same issue of a white splash screen being present. This was 6+ months later and a completely different project. Again, it was time to take to the power of a search and figure out what was going on. After a little time, I discovered this was a known problem with Hermes enabled and if I set my resolutionStrategy
for SoLoader then it would hopefully resolve. Thank you to GitHub issues and Alaa-Ben for a well-documented issue! I updated my app/build.gradle
file once again to include this and took to testing and validating locally with great success. Finally, I had confidence in uploading a bundle to the Play Console for consumption by my users. I have noted that the SoLoader version bump will be included in the React Native 0.62 release, I have seen it in the Release Candidate. In the meantime, this little fix will help move you forward with optimization.
The final test was to upload to the Play Console to determine just how much my file size was reduced with this bundling. This is just a small example of the improvement you could see, this does not include any performance benchmarking with the implementation of the Hermes engine. With our new bundle created and uploaded for this project, we went from a 21+MB APK to a ~7MB download size with the bundle. That is a ~69% reduction in file size!
I hope that my fellow React Native developers can benefit from my learnings and start to realize the benefits of these optimizations, as we have.
Contributor’s Bio
Jenn is a software engineer at Echobind, with a passion for improving processes. Wife and mom of 3, residing in rural Oregon. In her free time, she is an Emergency Medical Technician and Volunteers with Girls Who Code.