112

I want to use a existing native library from another Android project, so I just copied the NDK built library (libcalculate.so) to my new Android project. In my new Android project I created a folder libs/armeabi/ and put libcalculate.so there. There is no jni/ folder. My testing device has ARM architecture.

In my java code I load the library by:

  static{
    System.loadLibrary("calculate");
  }

When I run my new android project, I got error:

java.lang.UnsatisfiedLinkError:  ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libcalculate.so"

So, as error says, the copied native library is not in /verdor/lib or /system/lib , how to resolve this problem in my case?

(I unziped the apk package, under lib/ there is libcalculate.so)

====UPDATE=====

I also tried to create a jni/ folder under project root, and add an Android.mk file under jni/. The content of Android.mk is:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

Then, under project root, I executed ndk-build . After that, the armeabi/ and armeabi-v7a/ directories are generated by ndk-build (with libcalculate.so inside the folder).

Then I run my maven build the project successfully. In the final apk package, there are:

lib/armeabi/libcalculate.so
lib/armeabi-v7a/libcalculate.so

But when I run my app, the same error throw:

java.lang.UnsatisfiedLinkError:  ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libcalculate.so"
Robert
  • 39,162
  • 17
  • 99
  • 152
user842225
  • 5,445
  • 15
  • 69
  • 119
  • 3
    You put the library directly under `libs/`? You probably need to create one subdirectory per target ABI that you want to support (armeabi, armeabi-v7a, x86, mips, etc) and place the appropriate .so file in each subdirectory (i.e. the .so file built for armeabi goes in `libs/armeabi/`, etc). – Michael Dec 11 '14 at 11:28
  • @Michael , I just missed that in my post, I actually put it under libs/armeabi/ – user842225 Dec 11 '14 at 11:43
  • Check that libcalculate.so actually gets picked up by the packaging process - try e.g. `unzip -l package.apk`, or rename the apk to .zip and open it with some application. If it isn't there, something is wrong with packaging it (did your IDE notice the folder is there, do you need to refresh the project?). – mstorsjo Dec 11 '14 at 14:31
  • @mstorsjo, I unziped the apk package, under lib/ there is libcalculate.so – user842225 Dec 11 '14 at 18:41
  • libs is for Java libraries. You should be putting native libraries under lib (note no 's'). eg. lib/armabi/libcalculate.so – Richard Critten Dec 12 '14 at 22:18
  • 1
    you dont need to have an Android.mk or any compilation related files. Just put the so files in their according subdirectories to jniLibs like here: https://github.com/Ph1b/MaterialAudiobookPlayer/tree/master/audiobook/src/main – Paul Woitaschek Dec 15 '14 at 10:36
  • After use `REBUILT_SHARED_LIBRARY` try call the lib using `LOCAL_SHARED_LIBRARIES` – Alex Dec 15 '14 at 10:44
  • you can use libs even if it isn't best practice, it is the fastest way to test out a native lib--just make sure you use ABI directories and then sourceSets { main { jniLibs.srcDirs = ['libs'] } } see https://medium.com/@pokkbaby/android-studio-import-native-library-9dda32e58253 – Droid Teahouse Aug 23 '17 at 22:35

14 Answers14

195

To root cause (and maybe solve your issue in the same time), here is what you can do:

  1. Remove the jni folder and all the .mk files. You don't need these nor the NDK if you aren't compiling anything.

  2. Copy your libcalculate.so file inside <project>/libs/(armeabi|armeabi-v7a|x86|...) . When using Android Studio, it's <project>/app/src/main/jniLibs/(armeabi|armeabi-v7a|x86|...), but I see you're using eclipse.

  3. Build your APK and open it as a zip file, to check that your libcalculate.so file is inside lib/(armeabi|armeabi-v7a|x86|...).

  4. Remove and install your application

  5. Run dumpsys package packages | grep yourpackagename to get the nativeLibraryPath or legacyNativeLibraryDir of your application.

  6. Run ls on the nativeLibraryPath you had or on legacyNativeLibraryDir/armeabi, to check if your libcalculate.so is indeed there.

  7. If it's there, check if it hasn't been altered from your original libcalculate.so file: is it compiled against the right architecture, does it contain the expected symbols, are there any missing dependencies. You can analyze libcalculate.so using readelf.

In order to check step 5-7, you can use my application instead of command lines and readelf: Native Libs Monitor

PS: It's easy to get confused on where .so files should be put or generated by default, here is a summary:

  • libs/CPU_ABI inside an eclipse project

  • jniLibs/CPU_ABI inside an Android Studio project

  • jni/CPU_ABI inside an AAR

  • lib/CPU_ABI inside the final APK

  • inside the app's nativeLibraryPath on a <5.0 device, and inside the app's legacyNativeLibraryDir/CPU_ARCH on a >=5.0 device.

Where CPU_ABI is any of: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64. Depending on which architectures you're targeting and your libs have been compiled for.

Note also that libs aren't mixed between CPU_ABI directories: you need the full set of what you're using, a lib that is inside the armeabi folder will not be installed on a armeabi-v7a device if there are any libs inside the armeabi-v7a folder from the APK.

Warpzit
  • 27,966
  • 19
  • 103
  • 155
ph0b
  • 14,353
  • 4
  • 43
  • 41
  • 3
    Awesome man, thanks! I'm using Android Studio and my jni builds were being copied to libs instead of jniLibs. – Luis Dec 17 '14 at 17:59
  • 4
    That last note about needed the full set was crucial for me. That was my problem, thanks! – Ben Trengrove May 20 '15 at 14:18
  • For the `7` section: do you mean the .so might be changed from the APK after it has been installed on the device? If so would there be a chance the system may ruin the .so file? – jayatubi Oct 03 '15 at 05:55
  • no, but if you're supporting several architectures, maybe the wrong .so has been installed. – ph0b Oct 03 '15 at 06:50
  • 5
    Wonderful! In my very strange case, I was using a 3rd party set of libraries (OpenCV - in the **armeabi** folder) and those libraries stopped loading when I added a different 3rd party library via Gradle. It turns out that second library doesn't support ARMv5 or 6, and by including it, my OpenCV libraries became invisible (although they _were actually there_). Your point about the full set gave me the clue - renaming the armeabi folder and calling it **armeabi-v7a** fixed the problem (since I now don't support ARM 5 or 6...). Evil problem!! – Mete Nov 04 '15 at 13:56
  • Could you please convey us that this step ("In order to check step 6-8, you can use my application instead of command lines and readelf") is 5 to 7 instead of 6 to 8 – Sankar Ganesh PMP Nov 11 '15 at 06:22
  • @Mete, thank you - ran into exact same problem and you saved the day (or whatever is left of it!) – LNI Apr 11 '16 at 00:21
  • I found very useful the Native Libs Monitor from the store. Thank you ph0b. – peregrinus Jun 22 '16 at 10:52
  • 3
    Another way to find where to put your lib file (*.so) is to run your application and print the nativeLibraryDir using: System.out.println(getApplicationContext().getApplicationInfo().nativeLibraryDir), the name of the directory will also provide you with the ABI. – David Rauca Jun 10 '17 at 17:00
  • Great answer for *.so file importing issue in Android studio AAR building. – Lin Apr 29 '18 at 22:26
  • In Android Studio you may need to make sure that Deploy installation option is set to "APK from app bundle" rather than "Default APK". This option is located in Run -> Edit configurations menu. – Ivan Bychkov May 12 '22 at 22:53
26

In gradle, after copying all files folders to libs/

jniLibs.srcDirs = ['libs']

Adding the above line to sourceSets in build.gradle file worked. Nothing else worked whatsoever.

frogatto
  • 28,539
  • 11
  • 83
  • 129
18

The reason for this error is because there is a mismatch of the ABI between your app and the native library you linked against. Another words, your app and your .so is targeting different ABI.

if you create your app using latest Android Studio templates, its probably targeting the arm64-v8a but your .so may be targeting armeabi-v7a for example.

There is 2 way to solve this problem:

  1. build your native libraries for each ABI your app support.
  2. change your app to target older ABI that your .so built against.

Choice 2 is dirty but I think you probably have more interested in:

change your app's build.gradle

android {
    defaultConfig {
        ...
        ndk {
            abiFilters 'armeabi-v7a'
        }
   }
}
wdanxna
  • 10,699
  • 2
  • 23
  • 24
17

In my case i must exclude compiling sources by gradle and set libs path

android {

    ...
    sourceSets {
        ...
        main.jni.srcDirs = []
        main.jniLibs.srcDirs = ['libs']
    }
....
Gelldur
  • 11,187
  • 7
  • 57
  • 68
  • this solved for me as well and I added the files of armeabi in armeabi-v7a and x86 folders but I am not sure if it was necessary. – dokam_scotland May 24 '19 at 10:44
14

Are you using gradle? If so put the .so file in <project>/src/main/jniLibs/armeabi/

I hope it helps.

Assaf Gamliel
  • 11,935
  • 5
  • 41
  • 56
11

For reference, I had this error message and the solution was that when you specify the library you miss the 'lib' off the front and the '.so' from the end.

So, if you have a file libmyfablib.so, you need to call:

   System.loadLibrary("myfablib"); // this loads the file 'libmyfablib.so' 

Having looked in the apk, installed/uninstalled and tried all kinds of complex solutions I couldn't see the simple problem that was right in front of my face!

Andy Krouwel
  • 1,309
  • 11
  • 21
  • 1
    That was it. For some unknown reason Android does not install libraries whose filename doesn't start with 'lib', even if they are present in the package. Go figure... – George Y. Dec 19 '17 at 22:25
  • Where can I check this in the project? I mean where can I find this line `System.loadLibrary` in the code – aleksandrbel Aug 17 '18 at 08:38
  • Thanks. This helped ! – Riskhan Jan 21 '19 at 09:52
  • OH. MY. GOD! I've spent the whole day to solve it by checking the paths, but all was easy, thank you a lot for this! – Acuna Jul 21 '21 at 21:21
9

This is an Android 8 update.

In earlier version of Android, to LoadLibrary native shared libraries (for access via JNI for example) I hard-wired my native code to iterate through a range of potential directory paths for the lib folder, based on the various apk installation/upgrade algorithms:

/data/data/<PackageName>/lib
/data/app-lib/<PackageName>-1/lib
/data/app-lib/<PackageName>-2/lib
/data/app/<PackageName>-1/lib
/data/app/<PackageName>-2/lib

This approach is hokey and will not work for Android 8; from https://developer.android.com/about/versions/oreo/android-8.0-changes.html you'll see that as part of their "Security" changes you now need to use sourceDir:

"You can no longer assume that APKs reside in directories whose names end in -1 or -2. Apps should use sourceDir to get the directory, and not rely on the directory format directly."

Correction, sourceDir is not the way to find your native shared libraries; use something like. Tested for Android 4.4.4 --> 8.0

// Return Full path to the directory where native JNI libraries are stored.
private static String getNativeLibraryDir(Context context) {
    ApplicationInfo appInfo = context.getApplicationInfo();
    return appInfo.nativeLibraryDir;
}
AlgebraWinter
  • 321
  • 2
  • 3
5

Try to call your library after include PREBUILT_SHARED_LIBRARY section:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

#...

LOCAL_SHARED_LIBRARIES += libcalculate

Update:

If you will use this library in Java you need compile it as shared library

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(BUILD_SHARED_LIBRARY)

And you need deploy the library in the /vendor/lib directory.

Alex
  • 3,301
  • 4
  • 29
  • 43
3

You could just change ABI to use older builds:

defaultConfig {
    ...

    ndk {
        abiFilters 'armeabi-v7a'
    }
    ...
}

You should also use deprecated NDK by adding this line to gradle.properties:

android.useDeprecatedNdk=true
rezamm
  • 71
  • 1
  • 2
  • 9
1

actually, you can't just put a .so file in the /libs/armeabi/ and load it with System.loadLibrary. You need to create an Android.mk file and declare a prebuilt module where you specify your .so file as a source.

To do so, put your .so file and the Android.mk file in the jni folder. Your Android.mk should look something like that:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

Source : Android NDK documentation about prebuilt

Yannshu
  • 122
  • 4
1
defaultConfig {
ndk {
            abiFilters "armeabi-v7a", "x86", "armeabi", "mips"
    }
}

Just add these line in build.gradle app level

Savan Patel
  • 169
  • 8
0

In my experience, in an armeabi-v7a mobile, when both armeabi and armeabi-v7a directories are present in the apk, the .so files in armeabi directory won't be linked, although the .so files in armeabi WILL be linked in the same armeabi-v7a mobile, if armeabi-v7a is not present.

loopscn
  • 29
  • 6
0

please add all suport

app/build.gradle

ndk {
        moduleName "serial_port"
        ldLibs "log", "z", "m"
        abiFilters "arm64-v8a","armeabi", "armeabi-v7a", "x86","x86_64","mips","mips64"
}

app\src\jni\Application.mk

APP_ABI := arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64
Lion耿
  • 9
  • 2
  • 1
    Could you please be more specific about what to do? – EFrank Sep 14 '17 at 08:34
  • The .so file in you project. You should be support arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64. When I was do that, it work well. – Lion耿 Sep 20 '17 at 08:27
0

In my case, I simply delete the unused shared libraries, then the app can find the shared library. Specifically, I delete jniLibs/arm64-v8a/ and libs/arm64-v8a/ which were used in the app before, and only keep jniLibs/armeabi-v7a and libs/armeabi-v7a which are supposed to be used by the app.

jhuai
  • 67
  • 1
  • 4