lastminute.com logo

Technology

How to: enable Hermes JavaScript engine in your React Native app

fabrizio_duroni
fabrizio duroni
francesco_bonfadelli
francesco bonfadelli
marco_de_lucchi
marco de lucchi

React native 0.60.4 has a new cool feature for Android: a new JavaScript engine called Hermes. Let's see how you can turn it on in your React Native application to get all its benefits.


As you already may know, there has been some complains in the past related to the performance of React Native on the Android platform. One of the main reason was due to a big difference in the React Native architecture implementation between Android and iOS: the JavaScript engine used to execute your code. On iOS React Native uses the JavaScript Core engine exposed in the iOS SDK. On Android the SDK doesn’t offer the same feature, so the React Native Android implementation embeds a compiled version of the JavaScript Core engine. As a consequence of this fact the engine used on Android didn’t receive the regular updates that the iOS counterpart received on each system major update, and was also not optimized for React Native and generally speaking for executing JavaScript code for mobiles apps. This is the reason why the Facebook React Native team decided to create Hermes, an open source JavaScript engine optimized for mobile apps.
Which benefits does the new Engine bring to the table? As reported in the presentation blog post, there were a few key metrics kept in consideration by the Facebook React Native team:

For JavaScript-based mobile applications, user experience benefits from attention to a few primary metrics:

The time it takes for the app to become usable, called time to interact (TTI) The download size (on Android, APK size) Memory utilization

This new engine seems really cool!! Hermes is available starting from React Native 0.60.4. Now the question is: how can you start to use it? Let’s see how we enabled this cool new engine in the lastminute.com group mobile apps while we were doing the upgrade to the latest version of React Native in order to enable AndroidX in our apps.

Implementation

The first thing to do is to set the enableHermes option to true in the React Native project configuration. This is typically done in the build.gradle app file or, if you have one, in your react.gradle custom gradle file at app level.

project.ext.react = [
  /// ...other options...
  enableHermes: true
]

Then we need to tell to ProGuard (if you’re using it) to keep some Hermes classes.

-keep class com.facebook.hermes.unicode.** { *; }

In the official documentation these are all the steps needed to activate Hermes. So we added these configurations to our apps and we launched our app, but we got the following error:

2020-01-17 22:04:30.194 5745-6293/it.app E/SoLoader: couldn't find DSO to load: libhermes.so
2020-01-17 22:04:30.646 5745-6293/it.app E/AndroidRuntime: FATAL EXCEPTION: create_react_context
    Process: it.app, PID: 5745
    java.lang.UnsatisfiedLinkError: couldn't find DSO to load: libhermes.so
        at com.facebook.soloader.SoLoader.doLoadLibraryBySoName(SoLoader.java:738)
        at com.facebook.soloader.SoLoader.loadLibraryBySoName(SoLoader.java:591)
        at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:529)
        at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:484)
        at com.facebook.hermes.reactexecutor.HermesExecutor.<clinit>(HermesExecutor.java:20)
        at com.facebook.hermes.reactexecutor.HermesExecutorFactory.create(HermesExecutorFactory.java:27)
        at com.facebook.react.ReactInstanceManager$5.run(ReactInstanceManager.java:952)
        at java.lang.Thread.run(Thread.java:761)

As the error says, the compilation is failing because gradle is not able to find one the shared libraries used by Hermes. If you think well we are also missing a part in our setup: we said that React Native contains a compiled version of Hermes, but we are not telling to gradle where it can pick the aar file that contains it. Let’s fix this problem also with the help of the React Native upgrade tool.
First we need to add to the repository section in the main gradle file a new maven repository (that is contained in the node_modules of the app).

//....

allprojects {
    repositories {
      //....
      maven { url("$rootDir/../node_modules/jsc-android/dist") }
      //....
    }
}

//....

Then we need to declare the Hermes compiled version as dependencies in the build.gradle file.

//...

debugImplementation files("../../node_modules/hermes-engine/android/hermes-debug.aar")
qaReleaseImplementation files("../../node_modules/hermes-engine/android/hermes-release.aar")
releaseImplementation files("../../node_modules/hermes-engine/android/hermes-release.aar")

//...

As you can see we needed to link the aar version of Hermes specifically for each build variant we have. We also had to rename our qa flavor to qaRelease and link it to the hermes-release.aar file. Why? Because our QA build configuration inherits from the release one. The react.gradle contained in the React Native itself (node_modules/react-native/react.gradle) does some checks based on the flavor name and, if it contains release , it performs some additional operations for the release build types (for apps with Hermes enabled): _ generation of the sourcemap _ the removal of the debugger libraries (not needed for a release build). Below you can find the parts that do checks on the variant name.

//...

if (enableHermes) {
  doLast {
    def hermesFlags;
    def hbcTempFile = file("${jsBundleFile}.hbc")
    exec {
      if (targetName.toLowerCase().contains("release")) {
        // Can't use ?: since that will also substitute valid empty lists
        hermesFlags = config.hermesFlagsRelease
        if (hermesFlags == null) hermesFlags = ["-O", "-output-source-map"]
        } else {
          hermesFlags = config.hermesFlagsDebug
          if (hermesFlags == null) hermesFlags = []
        }

        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
          commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
      } else {
        commandLine(getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
      }
    }

    //....
  }
}

//...
def isRelease = targetName.toLowerCase().contains("release")
def libDir = "$buildDir/intermediates/transforms/"
def vmSelectionAction = {
    fileTree(libDir).matching {
        if (enableHermes) {
            // For Hermes, delete all the libjsc* files
            include "**/libjsc*.so"

            if (isRelease) {
                // Reduce size by deleting the debugger/inspector
                include '**/libhermes-inspector.so'
                include '**/libhermes-executor-debug.so'
            } else {
                // Release libs take precedence and must be removed
                // to allow debugging
                include '**/libhermes-executor-release.so'
            }
        } else {
            // For JSC, delete all the libhermes* files
            include "**/libhermes*.so"
        }
    }.visit { details ->
        def targetVariant = ".*/transforms/[^/]*/${targetPath}/.*"
        def path = details.file.getAbsolutePath().replace(File.separatorChar, '/' as char)
        if (path.matches(targetVariant) && details.file.isFile()) {
            details.file.delete()
        }
    }
}

Conclusion

Hermes is one of the cool new features contained in the new version of React Native emoji-sunglasses. Stay tuned for more updates and see how we are using all of them here at lastminute.com group.


About fabrizio duroni

fabrizio_duroni
Software Engineer

Fabrizio is a Software Developer with 15+ years of experience. He specialised in mobile application development, computer graphics and web development. He ❤️ computers 💻, music 🎸, tattoo, videogames 👾 and drawing ✏️. He is also the maintainer of this blog 👷‍.

About francesco bonfadelli

francesco_bonfadelli
Software Engineer

Francesco is a Software Engineer with 15+ years of experience. He specialises in mobile application development and backend development.

About marco de lucchi

marco_de_lucchi
Software Engineer

Marco is a Mobile Software Developer at lastminute.com, having joined the App Team in 2019. He is passionate about Apple and iOS development 📱, always focused on creating exceptional user experiences. Fun fact: you can quiz him about SwiftUI or Taylor Swift!


Read next

React Universe 2024

React Universe 2024

fabrizio_duroni
fabrizio duroni
sam_campisi
sam campisi

Let's dive into the talks from React Universe 2024 that stood out to us the most and share the key insights we gained. From innovative debugging tools to cross-platform development strategies, we’ll walk you through what we found valuable and how it’s shaping our approach to React and React Native development. [...]

Tech Radar As a Collaboration Tool

Tech Radar As a Collaboration Tool

rabbani_kajamohideen
rabbani kajamohideen

A tech radar is a visual and strategic tool used by organizations to assess and communicate the status and future direction of various technologies, frameworks, tools, and platforms. [...]