5

I am working on a mobile application and developing it using react-native.I can successfully run the project on my emulator via

react-native run-android 

and i can successfully have a bundle release with the command

./gradlew bundleRelease

And i can even successfully upload my app to the Google Play Store. The problem is that some devices won't be able to install the app.It will fail.

After searching on the internet i figured out that the problem was variety of different cpu architectures on different devices (as far as i understood).And my emulator is using a 32 bit cpu (x86) and the build runs successfully on my emulator.

I don't own an Android device so the best way i have found to debug the error was to use Firebase's "Test Lab" and observe the output.

I believe there is another problem other than cpu architecture, which is the Hermes.I am disabling the hermes in my app/build.gradle however when i release a bundle and test it on Firebase Test Lab the application will fail and give me this output;

java.lang.UnsatisfiedLinkError: couldn't find DSO to load: libhermes.so
     FATAL EXCEPTION: create_react_context
Process: com.lockerapp, PID: 18180
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:949)
    at java.lang.Thread.run(Thread.java:764)

Here is my environment;

System:
    OS: macOS 10.14.4
    CPU: (4) x64 Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
    Memory: 242.76 MB / 8.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 12.9.0 - /usr/local/bin/node
    Yarn: 1.17.3 - /usr/local/bin/yarn
    npm: 6.10.3 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: iOS 12.4, macOS 10.14, tvOS 12.4, watchOS 5.3
    Android SDK:
      API Levels: 25, 26, 27, 28, 29
      Build Tools: 26.0.2, 27.0.3, 28.0.3, 29.0.2
      System Images: android-28 | Intel x86 Atom_64, android-28 | Google Play Intel x86 Atom
  IDEs:
    Android Studio: 3.4 AI-183.6156.11.34.5692245
    Xcode: 10.3/10G8 - /usr/bin/xcodebuild
  npmPackages:
    react: 16.8.6 => 16.8.6 
    react-native: 0.60.5 => 0.60.5 
  npmGlobalPackages:
    create-react-native-app: 2.0.2
    react-native-cli: 2.0.1

And here is my app/build.gralde;

apply plugin: "com.android.application"

import com.android.build.OutputFile

project.ext.react = [
    entryFile: "index.js",
    enableHermes: false,  // clean and rebuild if changing
]

apply from: "../../node_modules/react-native/react.gradle"


def enableHermes = project.ext.react.get("enableHermes", false);

android {
    compileSdkVersion rootProject.ext.compileSdkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    defaultConfig {
        applicationId "com.lockerapp"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 11
        versionName "1.1.5"
    }
    splits {
        abi {
            reset()
            enable enableSeparateBuildPerCPUArchitecture
            universalApk false  // If true, also generate a universal APK
            include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
        }
    }
    signingConfigs {
        debug {
            storeFile file('debug.keystore')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        }
    release {
            if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
                storeFile file(MYAPP_UPLOAD_STORE_FILE)
                storePassword MYAPP_UPLOAD_STORE_PASSWORD
                keyAlias MYAPP_UPLOAD_KEY_ALIAS
                keyPassword MYAPP_UPLOAD_KEY_PASSWORD
            }
        }
    }
    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
        release {
            // Caution! In production, you need to generate your own keystore file.
            // see https://facebook.github.io/react-native/docs/signed-apk-android.
            signingConfig signingConfigs.release
        minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        }
    }
    // applicationVariants are e.g. debug, release
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            // For each separate APK per architecture, set a unique version code as described here:
            // https://developer.android.com/studio/build/configure-apk-splits.html
            def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
            def abi = output.getFilter(OutputFile.ABI)
            if (abi != null) {  // null for the universal-debug, universal-release variants
                output.versionCodeOverride =
                        versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
            }

        }
    }

    packagingOptions {
        pickFirst '**/armeabi-v7a/libc++_shared.so'
        pickFirst '**/x86/libc++_shared.so'
        pickFirst '**/arm64-v8a/libc++_shared.so'
        pickFirst '**/x86_64/libc++_shared.so'
        pickFirst '**/x86/libjsc.so'
        pickFirst '**/armeabi-v7a/libjsc.so'
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "com.facebook.react:react-native:+"  // From node_modules

    if (enableHermes) {
      def hermesPath = "../../node_modules/hermesvm/android/";
      debugİmplementation files(hermesPath + "hermes-debug.aar")
      releaseİmplementation files(hermesPath + "hermes-release.aar")
    } else {
      implementation jscFlavor
    }
}

// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
    from configurations.compile
    into 'libs'
}

apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

I am struggling with releasing a bundle for as many devices as possible and publishing to the Google Play Store.I don't really care about using hermes or not as far as my build succeeds and runs on all possible devices.

Is there a known guide that i might have missed to release a bundle for all cpu architectures and avoids the errors with hermes? Any answer is appreciated, thanks.

UPDATE:

After a lot of research i still couldn't find a solution for this, however i found a work around for the problem, instead of creating an .aab file with

./gradlew bundleRelase 

i am creating a universal APK file with the command;

./gradlew assembleRelease

The generated .apk file is accepted by google play store and i have passed the tests on Firebase Test Lab, also works on 32bit and 64bit local android emulators, the down side is of course my download size increased (from around 8mb to 15mb) but still it is a solution for a while.

Emre Bicer
  • 111
  • 3
  • 7
  • Related to https://github.com/facebook/react-native/issues/23764#issuecomment-518285719 ? – Pierre Sep 07 '19 at 13:47
  • Have u defined jscFlavor in your build.gradle file? – Anus Kaleem Sep 07 '19 at 13:48
  • @AnusKaleem it is defined as def jscFlavor = 'org.webkit:android-jsc:+' might have missed while writing my build.gradle file here, i tried to avoid comments as much as possible... – Emre Bicer Sep 07 '19 at 15:31
  • Can u analyse your apk using Android Studio to see if there is libhermes.so libs present in libs folder for each of the architecture? – Anus Kaleem Sep 07 '19 at 16:06
  • @AnusKaleem Initially i didn't have an APK since my goal is to come up with an aab, to have an APK i followed those steps in this link; https://facebook.github.io/react-native/docs/signed-apk-android , and after running ./gradlew assembleRelease i got my app-universal-release.apk , and when i inspect it as you told i found x86_64, arm64-v8a, x86, armeabi-v7a, armeabi under lib and under none of them libhermes.so is present. – Emre Bicer Sep 07 '19 at 17:45
  • It means hermes libs were not installed in first place... follow this answer and the answer following this, check if you are missing something here https://stackoverflow.com/a/57202275/3652368 – Anus Kaleem Sep 07 '19 at 18:17
  • @AnusKaleem I have seen this question at stackoverflow before and sadly my app/build.gradle is not missing anything from that answer... – Emre Bicer Sep 07 '19 at 19:00

1 Answers1

3

I was able to enabled hermes and also create the bundle and submmited to the playstore with this fix

if (getGradle().getStartParameter().getTaskRequests().toString().toLowerCase().contains("bundle")) {
    packagingOptions {
        exclude '**/libhermes-inspector.so'
        exclude '**/libhermes-executor-debug.so'
        exclude '**/libjscexecutor.so'
    }
}

https://github.com/facebook/react-native/issues/25927#issuecomment-535695869

if you want to test the final bundle file I recommend couple of things:

  1. Download the android bundle tool (BundleTool GitHub)
  2. Create a profile from one android device

    java -jar bundletool.jar get-device-spec --output=device-spec.json

  3. Generate the apks for that specific profile

    java -jar bundletool.jar build-apks --device-spec=device-spec.json --bundle=app-release.aab --output=device.apks

  4. Install the generated APK into the device

    java -jar bundletool.jar install-apks --apks=device.apks

  5. Also, you could use the android apk analyzer and take a look into those APK and ensure that the hermes binary is there and the libjs related binary isn't.

BlaShadow
  • 11,075
  • 7
  • 39
  • 60
  • 1
    Note: adb does not install .apks files, however bundletool does. – Pierre Nov 01 '19 at 09:25
  • @Pierre use adb install -r -t app-release.apk to install apks ;) – Alex Lévy Jan 21 '20 at 17:42
  • No, ADB installs .apk files, not .apks files. Note the additional "s". – Pierre Jan 21 '20 at 22:05
  • 1
    Thank you! I was having the hardest time trying to figure out why Google Play was showing the following error after uploading my app bundle: "The following APKs or App Bundles are available to 64-bit devices, but they only have 32-bit native code". I triple checked that all of the 64-bit libs were present inside of the aab file. Running bundletool shows: "WARNING: App Bundle contains 32-bit RenderScript bitcode file (.bc) which disables 64-bit support in Android. 64-bit native libraries won't be included in generated APKs." – Daniel Chesher Feb 15 '20 at 02:52