We have a ReactNative application that has integrated Unity based on react-native-unity-view successfully as a library for quite a long time. However, after most recent updates on ReactNative regarding AndroidX and latest SDK-changes, we're running into the issue "libmain.so not found". This exception happens on the very first attempt to display a Unity-View.
There is a similar issue on SO, please don't mark as duplicate as our approach might be different and particulary since we give many many more details here.
There is a running and still working example on GitHub (based on ReactNative 0.57): https://github.com/f111fei/react-native-unity-demo. As long as this project is based on ReactNative 0.57 and its appropriate gradle-settings, everything works fine. But as soon as we upgrade to the latest ReactNative version (and even 0.60 for example), which gives us an android-folder that is very different against to the older one), the issue happens with a crash of the application. (We also tried to set up everything from scratch to exclude the possibility that this issue was due to upgrades. With RN 0.57 everything still works fine, with RN 0.60 and above not)
The exception is as follows:
E Unity : Failed to load 'libmain.so', the application will terminate.
D AndroidRuntime: Shutting down VM
E AndroidRuntime: FATAL EXCEPTION: main
E AndroidRuntime: Process: com.rnunitydemo, PID: 16887
E AndroidRuntime: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.rnunitydemo-bKGyotdcwjVnBxuR9zLE4Q==/base.apk"],nativeLibraryDirectories=[/data/app/com.rnunitydemo-bKGyotdcwjVnBxuR9zLE4Q==/lib/arm64, /data/app/com.rnunitydemo-bKGyotdcwjVnBxuR9zLE4Q==/base.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]] couldn't find "libmain.so"
First I'm going to list the android-folder and gradle-files from most recent version, where the crash happens. After that, I will list the files where everything works fine:
./android/build.gradle:
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 28
}
repositories {
google()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:3.4.2")
}
}
allprojects {
repositories {
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
./android/settings.gradle:
rootProject.name = 'rnunitydemo'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ":UnityExport"
project(":UnityExport").projectDir = file("./UnityExport")
include ':app'
./android/app/build.gradle:
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 enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
def jscFlavor = 'org.webkit:android-jsc:+'
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.rnunitydemo"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
}
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'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.debug
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
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.facebook.react:react-native:+" // From node_modules
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}
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)
./android/UnityExport/build.gradle:
// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:3.4.2")
}
}
allprojects {
repositories {
google()
jcenter()
flatDir {
dirs 'libs'
}
}
}
apply plugin: 'com.android.library'
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
}
android {
compileSdkVersion 28
buildToolsVersion '28.0.2'
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
multiDexEnabled true
versionCode 1
versionName '0.1'
}
lintOptions {
abortOnError false
}
aaptOptions {
noCompress = ['.unity3d', '.ress', '.resource', '.obb']
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
}
buildTypes {
debug {
minifyEnabled false
useProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-unity.txt'
signingConfig signingConfigs.debug
jniDebuggable true
}
release {
minifyEnabled false
useProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-unity.txt'
signingConfig signingConfigs.debug
}
}
packagingOptions {
doNotStrip '*/armeabi-v7a/*.so'
doNotStrip '*/x86/*.so'
}
}
All these files above lead to a crash ("libmain.so not found") as soon as Unity will be launched.
In our previous version, as it can be found on GitHub, everything went fine:
./android/build.gradle:
buildscript {
ext {
buildToolsVersion = "27.0.3"
minSdkVersion = 16
compileSdkVersion = 27
targetSdkVersion = 26
supportLibVersion = "27.1.1"
}
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
}
}
allprojects {
repositories {
mavenLocal()
jcenter()
google()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
flatDir {
dirs project(':UnityExport').file('libs')
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '4.4'
distributionUrl = distributionUrl.replace("bin", "all")
}
./android/settings.gradle:
rootProject.name = 'rnunitydemo'
include ':react-native-unity-view'
project(':react-native-unity-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-unity-view/android')
include ":UnityExport"
project(":UnityExport").projectDir = file("./UnityExport")
include ':app'
./android/app/build.gradle:
apply plugin: "com.android.application"
import com.android.build.OutputFile
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.rnunitydemo"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86"
}
}
signingConfigs {
release {
// storeFile file(MYAPP_RELEASE_STORE_FILE)
// storePassword MYAPP_RELEASE_STORE_PASSWORD
// keyAlias MYAPP_RELEASE_KEY_ALIAS
// keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
buildTypes {
debug {
manifestPlaceholders = [
"DEBUGGABLE": "true"
]
}
release {
manifestPlaceholders = [
"DEBUGGABLE": "false"
]
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
signingConfig signingConfigs.release
}
}
// 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:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a":1, "x86":2]
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
}
}
}
}
dependencies {
compile project(':react-native-unity-view')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
}
// 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'
}
./android/UnityExport/build.gradle:
(same as above)
There are some other reports according to this issue in the official Unity2D-Forums, but without any working solution. According to a comment we are aware of using the very same abiFilter-Setting in both build.gradle-Files (this from our app and the build-gradle from UnityExport), but it didn't solve the issue either.
Can someone help please?
** Update **
System information of development machine:
System:
OS: macOS Mojave 10.14.6
CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
Memory: 112.35 MB / 32.00 GB
Shell: 5.0.11 - /usr/local/bin/bash
SDKs:
iOS SDK:
Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1
Android SDK:
API Levels: 23, 25, 26, 27, 28, 29
Build Tools: 23.0.1, 25.0.0, 25.0.1, 25.0.2, 26.0.1, 27.0.3, 28.0.3, 29.0.0, 29.0.2
System Images: android-29 | Google APIs Intel x86 Atom
Android NDK: 20.0.5594570
IDEs:
Android Studio: 3.5 AI-191.8026.42.35.5900203
Xcode: 11.2.1/11B500 - /usr/bin/xcodebuild
Binaries:
Node: 10.17.0 - /usr/local/opt/node@10/bin/node
Yarn: 1.16.0 - /usr/local/bin/yarn
npm: 6.11.3 - /usr/local/opt/node@10/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
npmPackages:
react: 16.9.0 => 16.9.0
react-native: 0.61.4 => 0.61.4
npmGlobalPackages:
react-native-cli: 2.0.1
I can answer by myself now. The issue was caused by Unity export that was not ARM64-compatible unless several settings were applied manually. Here is how:
Bevor building for Android, we need to adjust some settings in "Player Settings" in order to make ARM64 available:
If you run into an Error like "NDK not found" while building, ensure that Unity has downloaded and installed its own copy of NDK:
Having all this in mind will enable us to link against these libraries without failing to find "libmain.so".
Please add following line into your string.xml
<string name="game_view_content_description">Game view</string>
After that add below snippet into your app level's gradle file
defaultConfig {
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
}
That's it. Enjoy coding!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With