This post has been republished via RSS; it originally appeared at: Microsoft Mobile Engineering - Medium.
Microsoft Teams hosts internal React Native applications via an SDK through which these React Native applications can gain access to Teams’ resources. Thus, it is fundamental for us to keep up with the latest releases of React Native to leverage the latest and greatest that React Native has to offer.
Microsoft Teams is essentially a brownfield application and upgrading React Native versions often gets tricky. One of the biggest challenges of upgrading React Native is to keep the Android app size in check with each upgrade. Larger app sizes often translate to lower user engagement and lower number of downloads. So it is of utmost importance that we keep the app size in check.
Recently, at Microsoft Teams we upgraded React Native from 0.64.2 to 0.68.3. We started off with a 1.2 MB increase in app size and we were able to bring that down to 0.48 MB. This article talks about the process we followed and steps we took to cut down on the app size.
Why did the size increase?
After upgrading to React Native 0.68.3, we were looking at an Android app size diff of +1.2 MB. That is a significant increase. The first and foremost question we asked ourselves is why? as in why did the size increase.
The easiest way to check what has caused a size increase is to compare a before and after Android apps.
In other words, we generated two Android apps, one was the older app with React Native 0.64.3 and the other one was the newer app with React Native 0.68.3. Android studio provides a handy “Analyze APK” (Analyze your build with APK Analyzer | Android Developers) functionality which gives us a comprehensive look at all the contents of the app and their sizes. We can even compare two different apps and look at the “diff” between them.
Upon comparing the old and the new apps, we figured out that most of the size increase was from some newly added Shared Libraries (Use prebuilt libraries | Android NDK | Android Developers) by React Native and some pre-existing shared libraries namely, “reactnativeutilsjni.so” and “reactnativejni.so” which had significantly increased in size since the 0.64.2 version.
Do we need all these newly added Shared Libraries?
After looking at all the newly added Shared Libraries, the next question we asked ourselves was “Do we need all of these Shared Libraries?”. Shared Libraries are required at runtime and hence if there are some modules in react native that we do not use, there is no need to maintain those shared libraries.
React Native 0.68 is the first version of react native which ships with React Native’s new architecture featuring Fabric, the new renderer and Turbo Modules. In 0.68 this new architecture was opt-in. We as a team took the decision not to enable the new architecture with this upgrade and to continue to use the old architecture.
Thus theoretically, any new Shared Libraries added by React Native which are exclusive to either Fabric or Turbo Modules can be removed from the Android app and it would not have any effect because we are not using the new architecture.
Let’s remove the Shared Libraries that we think we don’t need.
The next step would be to identify and remove the Shared Libraries that we think we won’t need.
To identify the .so files that can be removed, we needed to look closely into React Native’s code.
In Android, Shared Libraries are generally built and referenced in Android.mk files which reside inside a project’s /jni directory. More about Android.mk here: Android.mk | Android NDK | Android Developers
The next step was a bit tedious. We had to pick each of the newly added .so file and check for references across all the “Android.mk” files in React Native’s repository.
The logic was simple. If we find a .so file which is only referenced in a library or module inside React Native which we are not going to use, we can discard that .so file.
We found out that certain Shared Libraries like “libfabricjni” and “libturbomodulejsijni” were used only in modules which are required by the new architecture. Hence, we could directly exclude these Shared Libraries while bundling our android app. There were some other libraries which were only referenced by “libfabricjni” and “libturbomodulejsijni”. We removed those too and that got the android app size down to 0.7 MB (about 500 KB improvement over 1.2 MB that we started off with).
Having brought the size down significantly, we were keen to see if we could further improve on the size aspect.
Interestingly, while doing the above-mentioned exercise, we saw that two libraries, namely “reactnativejni” and “reactnativeutilsjni” were being built from the same sources. Essentially, “reactnativeutilsjni” was a subset of “reactnativejni”. Look here: Let’s not build reactnativeutilsjni shared library by SparshaSaha · Pull Request #34345 · facebook/react-native (github.com)
Thus, we raised a PR(Let’s not build reactnativeutilsjni shared library by SparshaSaha · Pull Request #34345 · facebook/react-native (github.com) ) on React Native where we stopped generating “reactnativeutilsjni” and replaced all its references with “reactnativejni” in react-native’s codebase. React Native 0.68.3 onwards, every app shipped with React native should see an app size decrease of about 220 KB because of this change.
With this improvement we could get the app size down by another 220 KB and are currently standing at a diff of about +0.48 MB.
With the size diff down to an acceptable +0.48 MB, we were able to move ahead with our upgrade. It is important to note that all the Shared Libraries we excluded will need to be added back when we enable React Native’s new architecture thus adding back all the bytes we worked so hard to remove.
Maybe we will be able to clean up some files relating to “old architecture” when we decide to go with the “new architecture” but that’s a problem for another day.
Keeping Android app size in check with React Native Upgrades was originally published in Microsoft Mobile Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.