React Native — makes Android Devs crazy

Oleksandr Kucherenko
6 min readDec 10, 2018

Another short story about React Native approaches that can make Android guys crazy in several minutes.

Intro

Some time ago I joined the great team that develops a solution in ReactNative for a quite long time. The solution is quite big and we receive updates to project almost every hour - new features and bug fixes. Anyway, I was involved as an Android expert for making developer experience better, build process smooother and make developer life great again.

First Learning Steps — React Native Approaches

I will not discuss the architecture of React Native (RN) itself, I will focus on the topic “react native modules”. This is reusable code and quite often it has a “native” implementation, code that on Java/Kotlin level execute some API or enables access to some Android platform specifics.

The approach is great and I see that it’s working quite well for RN developers for the past several years.

The hell raised up several months ago when we realize that solution should be upgraded to the latest AndroidX support library and we should completely review the build scripts of the android application.

Issues as is — no blames, just a fact that things are broken from Android developer perspective.

Photo by Adi Goldstein on Unsplash

I will mix “root Project” and “solution” keywords in the text below, but it means the same thing actually. “Root Project” is more a gradle terminology.

Almost all react native modules that introduce something for an application (for example a fingerprint scanner functionality) are connected to project via source code. And at some level its really good, you can attach a debugger and see what actually happens in code and fix it if needed. The devil is in how this source code integrated into the solution:

a) root of the solution is not an android build project with the application as a sub-module. In RN world its mirrored — root project is in depth of folders, and all submodules are located several folders upper in the hierarchy. As a result for attaching sub-module RN developer should always modify settings.gradle file and include sub-module and in addition to that always reconfigure it folder relative to root project.

projectDir is always a subject of change in RN world for sub-modules

I can offer a better solution:

  • when developer add RN module anyway executed some integration script, why not make a symbolic link to a folder instead? As result will be much easier navigation and project structure, that is more matching Android Native developer project structures.
Create a symbolic link to a needed sub-module in a nested folder relative to root Project
Copy vs Symbolic Link, Inner folder vs Outside Parent folder

b) In RN world all submodules are not developed as common android library modules. As a result, we have build.gradle inside the module folder that combines at the same time configuration of “root project” and library. The mix of responsibilities and an unneeded increase of complexity. And it becomes even worst when a developer of the module wants to provide an android app sample(s) as a part of the module. Again this mirrored folders logic make things more complex than it should be. Just a small example of common troubles that RN approach gives us — https://github.com/react-native-community/react-native-image-picker/issues/999

Better solution:

  • RN module should contain build.gradle and settings.gradle files in the root of the module and inside them should be configured build plugins needs and sub-modules. As a result, we will get simplified structure, easier way of attaching library code to a root project, easier way to configure/override library dependencies. Plugins configuration will not interfere with “root project” plugins configuration and versions updates.
react-native-module/
+-- library/
| +-- build.gradle
| +-- src/
+-- examples/
| +-- example1/
| +-- build.gradle
| +-- ...
| +-- exampleN/
| +-- ...
+-- build.gradle
+-- settings.gradle
+-- ...
+-- package.json
  • even if you are not happy about keeping all the files in the proposed structure, you should be open for several it variations: android/ folder and inside it should be library code and root project build.gralde files. The main idea behind that — “it should be possible to build the library from the command line and deploy it to maven repository”.
react-native-module/
+-- android
| +-- library/
| +-- example1/
| +-- gradle/wrapper/...
| +-- build.gradle
| +-- settings.gradle
+-- package.json
+-- ...

c) It is quite shocking that RN developers ignore gradle binary dependencies resolving approach. It is one of the best features of gradle and it used in so bad way. Quite often I see in modules no “in-depth” understanding of the gradle configurations in combination with outdated plugin syntax/structure.

Use “api” for referencing RN framework APIs
  • use api (or provided) syntax for referencing RN framework in modules, please do NOT include a dependency to RN as a implementation (or compile) thing. It’s a wrong from logical and structuring points.

api — is a reference on code that we expect will be attached by “root project” (that what makes library small in size!), implementation — is the demand to include framework into final library binary (and this is known as a fat JAR approach).

  • delivery of the sub-modules for “react native” application can be done in “binary” way — via AAR or JAR files. Benefits are: its faster and allow to compile everything in seconds instead of minutes; incremental compilation helps a lot and reduce the time needed for compilation, but still if possible we should use binaries, instead of source code. Our disks are big and inside the source folder can be a million of files, but why we should stress our systems if we can avoid that?! One more point — that allows to decouple android native code from JavaScript part and fix in native part can be delivered separately from NPM package.

d) Configurations hell. React Native projects push that problem on a next level.

Version Hell == NPM + Yarn + Pods + Gradle + Maven

Photo by Jon Tyson on Unsplash

Seriously in case of any problem with code first thing you should do is to drop all caches, and kill all the build systems daemons. After that, you can try to redownload everything and try to build. If you are lucky all will pass in 5 minutes without any problems.

The build pipeline is so un-optimized for Android, that it stimulates all RN developers to test everything on iOS only.

And don’t forget that Android also have own “hell” with devices hardware configurations — phones, phablets, tablets, TVs, Chromebooks, usb/hdmi dongles, etc.

As result: Versions Hell + Devices Hell leads to significantly reduced support and efforts investments into Android OS integration. iOS and Android are close in functionality and performance to each other, they are close in hardware and software features, and only developer experience leads that Android losing a battle in React Native world. Android become a second type citizen in RN world even when customers are divided 50/50% between those platforms. Developer — wins, Customer — loss.

e) I have to confirm that most of the react-native components stuck in Java 7 and never hear about Java 8, Kotlin, RxJava and many other best tools of the Android world. In the best case, RN modules are starting adopting 6 months old versions of Android build tools. No unit tests, no deep optimizations.

In addition, Google is not standing in one place and every two weeks delivers something new. And RN world — is simply not so dynamic as it should be.

The solution for (d) and (e): RN world needs more pro-active Android developers that will help with proper integration and optimizations.

Photo by Farzad Nazifi on Unsplash

I think this is enough for a quick reflection on RN world from Android developer perspective. RN world has much complexity and as an Android developer, I see a lot of opportunities to help them in achieving better results. Let’s make Android a great platform for RN!

--

--

Oleksandr Kucherenko

Full-stack + Android Pro. My dev path: 2x Klarna | Desifer | 2x Spotify | EasyPark| Telia | Veroveli | TrueCaller | ArtfulBits