1

After switching to Android Studio, I began seeing the dreaded

java.lang.UnsatisfiedLinkError: dlopen failed: library
'/data/app-lib/com.myapp.test-1/libmylib.so' not found 

error. When I unpack the apk, I can see libmylib.so along with all the other native libraries (libmyotherlib.so and libtest.so) under the lib/armeabi folder, so packaging shouldn't be the problem... I decided to root my test device and check out the actual contents of my app's folder under /data/app-lib, where its native libraries should be after install -- I found that one of my app's native libraries (libmylib.so) was missing after the app is installed on a device. libmylib.so and libmyotherlib.so are pre-built .so files placed in src/main/jniLibs, and libtest.so is compiled from test.c in src/main/jni.

This only started after I switched to Android Studio; I've verified that apks built from the same code in Eclipse ADT have all the required libraries present under /data/app-lib/com.myapp.test-1 after installation.

Relevant build.gradle for Android Studio build:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "com.myapp.test"
        minSdkVersion 17
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

        ndk{

            moduleName "test"//testing ndk integration
        }

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
        }
    }

}

repositories {
    // You can also use jcenter if you prefer
    mavenCentral()
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'

    //android support libs etc.
    compile 'com.android.support:appcompat-v7:23.1.0'
    compile 'com.android.support:support-v13:23.1.0'

}

Relevant gradle.properties file

android.useDeprecatedNdk=true

Under src/main/jni I have only test.c

#include <jni.h>

int main(){

    return 0;
}

and under src/main/jniLibs/armeabi I have

libmylib.so
libmyotherlib.so

Relevant Android.mk for Eclipse ADT build:

include $(CLEAR_VARS)
LOCAL_MODULE    := test
LOCAL_SRC_FILES += test.c
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := myotherlib
LOCAL_SRC_FILES :=  $(TARGET_ARCH_ABI)/libmyotherlib.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := mylib
LOCAL_SRC_FILES :=  $(TARGET_ARCH_ABI)/libmylib.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)

Relevant /data/app-lib contents after installing app built by Android Studio:

root@SOLONE SL-K40:/ # ls /data/app-lib/com.myapp.test-1               
libmyotherlib.so
libtest.so

Relevant /data/app-lib contents after installing app built by Eclipse ADT:

root@SOLONE SL-K40:/ # ls /data/app-lib/com.myapp.test-1               
libmylib.so
libmyotherlib.so
libtest.so

I accidentally found that by adding

sourceSets {
        main {
            jni.srcDirs = []
 }
}

to my build.gradle I can get libmylib.so to show up again after installation, but this precludes having any NDK source code in my project.

First Question: Any idea what might be going on here? Could it be that mylib was actually compiled for an abi other than armeabi and Android is discarding it because its actual abi doesn't match the folder it came in inside the apk (I don't have the source code for mylib)? My problem sounds similar to one discussed here, but that person seemed to see only one shared library in the final installed app; I'm seeing all but one of my shared libraries.

Second Question: What is the current correct way to include pre-built .so files in an Android Studio build? Clues around the 'net seem to vary wildly by Android Studio version (I'm using Android Studio 1.5, Gradle version 2.4, Android plugin version 1.3.0) Is there still a need to redirect the jniLibs.srcDir variable to src/main/libs?

Community
  • 1
  • 1
CCJ
  • 1,437
  • 24
  • 35
  • my guess is that its some gradle task that is mucking the build – Bhargav Dec 02 '15 at 19:43
  • first of all, you don't need root to access the libraries in **/data/app-lib/com.myapp.test-1/** – Alex Cohn Dec 02 '15 at 20:50
  • 1
    @AlexCohn true, but you do need root to list the contents of /data/app-lib via adb shell 'ls -al /data/app-lib', and I didn't want to have to get my app's folder's name at runtime from Context::getApplicationInfo().nativeLibraryDir – CCJ Dec 02 '15 at 21:03
  • 1
    @CCJ you dont need to if you are running debug app: http://stackoverflow.com/a/7712173/4758618 – V-master Dec 03 '15 at 07:46

1 Answers1

2

Yes, the best approach is to define jniLibs.srcDir such that all prebuilt libraries can be copied from there.

Yes, the ABI is the most probable source of the problem. If the prebuilt library was built for armeabi but the device (as the vast majority of devices today) supports armeabi-v7a, then the installer will happily copy the …v7a versions of your not-prebuilt libraries to /data/app-lib/com.myapp.test-1, and the loader will later complain that libmylib.so is missing.

You should instruct Android Studio to only build one ABI. If you did not do something special in Eclipse build, then this is most likely armeabi.

The way to instruct AS depends on the version of gradle plugin.

For 'com.android.tools.build:gradle:1.5.0' plugin, I use something like

android {
    defaultConfig.ndk {
        …
        abiFilter 'armeabi'
    }

    splits {
        abi {
            enable true
            reset()
            include 'armeabi'
        }
    }
}

For …gradle-experimental:0.2.0, I use

model {
    android.ndk {
        …
        abiFilters += 'armeabi'
    }
}

I didn't have a need to enable splits on the experimental plugin, so I will not mislead you about the syntax changes there.

Alex Cohn
  • 52,705
  • 8
  • 94
  • 269
  • huh, weird. I wonder how/why libmyotherlib made it past the install process then? The final apk had a libtest for all abis, including armeabi-v7a, but libmyotherlib was only present in armeabi... – CCJ Dec 04 '15 at 16:14
  • 1
    could the installer have detected that there was a library called libmylib available in the device's /system/lib directory? If so, that could explain why libmyotherlib made it through installation and libmylib was thrown out -- the actual library I'm loading is a modified version of one found under /system/lib on my test device, so that and/or the abi best-fit filtration you mentioned would explain the behavior I saw – CCJ Dec 04 '15 at 16:42