System.loadLibrary (…) không thể tìm thấy thư viện gốc trong trường hợp của tôi


91

Tôi muốn sử dụng thư viện gốc hiện có từ một dự án Android khác , vì vậy tôi vừa sao chép thư viện được xây dựng NDK ( libcalculate.so ) vào dự án Android mới của mình. Trong dự án Android mới của mình, tôi đã tạo một thư mục libs/armeabi/và đặt libcalculate.so ở đó. Không jni / thư mục. Thiết bị thử nghiệm của tôi có kiến ​​trúc ARM.

Trong mã java của tôi, tôi tải thư viện bằng cách:

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

Khi tôi chạy dự án Android mới của mình, tôi gặp lỗi:

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

Vì vậy, như lỗi đã nói, thư viện gốc được sao chép không nằm trong / verdor / lib hoặc / system / lib, làm thế nào để giải quyết vấn đề này trong trường hợp của tôi?

(Tôi đã giải nén gói apk, dưới lib / có libcalculate.so)

==== CẬP NHẬT =====

Tôi cũng đã cố gắng tạo thư mục jni / dưới gốc dự án và thêm tệp Android.mk trong jni /. Nội dung của Android.mk là:

LOCAL_PATH := $(call my-dir)

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

Sau đó, dưới gốc dự án, tôi thực thi ndk-build. Sau đó, thư mục armeabi / và armeabi-v7a / được tạo bởi ndk-build (với libcalculate.so bên trong thư mục).

Sau đó tôi chạy maven xây dựng dự án thành công. Trong gói apk cuối cùng, có:

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

Nhưng khi tôi chạy ứng dụng của mình, cùng một lỗi:

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

3
Bạn đặt thư viện trực tiếp dưới libs/? Bạn có thể cần tạo một thư mục con cho mỗi ABI mục tiêu mà bạn muốn hỗ trợ (armeabi, armeabi-v7a, x86, mips, v.v.) và đặt tệp .so thích hợp trong mỗi thư mục con (tức là tệp .so được xây dựng cho armeabi đi kèm libs/armeabi/, Vân vân).
Michael

@ Michael, tôi chỉ nhớ rằng trong bài viết của tôi, tôi thực sự đặt nó dưới libs / armeabi /
user842225

Kiểm tra xem libcalculate.so có thực sự được chọn bởi quá trình đóng gói hay không - hãy thử ví dụ: unzip -l package.apkhoặc đổi tên apk thành .zip và mở nó bằng một số ứng dụng. Nếu nó không có ở đó, có gì đó sai khi đóng gói nó (IDE của bạn có nhận thấy thư mục ở đó không, bạn có cần phải làm mới dự án không?).
mstorsjo

@mstorsjo, tôi unziped gói apk, dưới lib / có libcalculate.so là
user842225

1
bạn không cần phải có Android.mk hoặc bất kỳ tệp nào liên quan đến biên dịch. Chỉ cần đặt các tệp như vậy vào thư mục con của chúng tới jniLibs như ở đây: github.com/Ph1b/MaterialAudiobookPlayer/tree/master/audiobook/…
Paul Woitaschek

Câu trả lời:


177

Để giải quyết nguyên nhân gốc rễ (và có thể giải quyết vấn đề của bạn cùng lúc), đây là những gì bạn có thể làm:

  1. Xóa thư mục jni và tất cả các tệp .mk . Bạn không cần những thứ này cũng như NDK nếu bạn không biên dịch bất cứ thứ gì.

  2. Sao chép libcalculate.sotệp của bạn vào bên trong <project>/libs/(armeabi|armeabi-v7a|x86|...). Khi sử dụng Android Studio, <project>/app/src/main/jniLibs/(armeabi|armeabi-v7a|x86|...)nhưng tôi thấy bạn đang sử dụng eclipse.

  3. Tạo APK của bạn và mở nó dưới dạng tệp zip , để kiểm tra xem libcalculate.sotệp của bạn có bên trong lib / (armeabi | armeabi-v7a | x86 | ...) .

  4. Xóa và cài đặt ứng dụng của bạn

  5. Chạy các gói gói bãi rác | grep yourpackagename để lấy nativeLibraryPath hoặc inheritNativeLibraryDir của ứng dụng của bạn.

  6. Chạy ls trên nativeLibraryPath bạn đã có hoặc trên inheritNativeLibraryDir / armeabi , để kiểm tra xem libcalculate.so của bạn có thực sự ở đó hay không.

  7. Nếu nó ở đó, hãy kiểm tra xem nó có bị thay đổi từ tệp libcalculate.so ban đầu của bạn không : nó có được biên dịch theo đúng kiến ​​trúc không, nó có chứa các ký hiệu mong đợi không, có bất kỳ phụ thuộc nào bị thiếu không. Bạn có thể phân tích libcalculate.so bằng cách sử dụng readelf.

Để kiểm tra bước 5-7, bạn có thể sử dụng ứng dụng của tôi thay vì các dòng lệnh và tự đọc: Native Libs Monitor

Tái bút: Thật dễ nhầm lẫn về nơi các tệp .so nên được đặt hoặc tạo theo mặc định, đây là bản tóm tắt:

  • libs / CPU_ABI bên trong dự án nhật thực

  • jniLibs / CPU_ABI bên trong một dự án Android Studio

  • jni / CPU_ABI bên trong AAR

  • lib / CPU_ABI bên trong APK cuối cùng

  • bên trong nativeLibraryPath của ứng dụng trên thiết bị <5.0 và bên trong kế thừa của ứng dụngNativeLibraryDir / CPU_ARCH trên thiết bị> = 5.0.

Trong đó CPU_ABI là bất kỳ trong số: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64 . Tùy thuộc vào kiến ​​trúc bạn đang nhắm mục tiêu và lib của bạn đã được biên dịch cho.

Cũng lưu ý rằng lib không được trộn lẫn giữa các thư mục CPU_ABI: bạn cần có bộ đầy đủ những gì bạn đang sử dụng, lib nằm trong thư mục armeabi sẽ không được cài đặt trên thiết bị armeabi-v7a nếu có bất kỳ lib nào bên trong armeabi -v7a thư mục từ APK.


3
Người đàn ông tuyệt vời, cảm ơn! Tôi đang sử dụng Android Studio và các bản dựng jni của tôi đã được sao chép sang libs thay vì jniLibs.
Luis

4
Lưu ý cuối cùng về sự cần thiết của bộ đầy đủ là rất quan trọng đối với tôi. Đó là vấn đề của tôi, cảm ơn!
Bến Trengrove

Đối với 7phần: ý của bạn là .so có thể được thay đổi từ APK sau khi nó đã được cài đặt trên thiết bị? Nếu vậy thì có khả năng hệ thống làm hỏng tệp .so không?
jayatubi

5
Tuyệt vời! Trong trường hợp rất lạ của tôi, tôi đang sử dụng bộ thư viện của bên thứ 3 (OpenCV - trong thư mục armeabi ) và những thư viện đó đã ngừng tải khi tôi thêm một thư viện bên thứ 3 khác thông qua Gradle. Nó chỉ ra rằng thư viện thứ hai không hỗ trợ ARMv5 hoặc 6, và bằng cách bao gồm nó, các thư viện OpenCV của tôi trở nên vô hình (mặc dù chúng thực sự ở đó ). Quan điểm của bạn về bộ đầy đủ đã cho tôi manh mối - đổi tên thư mục armeabi và gọi nó là armeabi-v7a đã khắc phục sự cố (vì hiện tại tôi không hỗ trợ ARM 5 hoặc 6 ...). Vấn đề xấu xa !!
Mete

1
Một cách khác để tìm nơi đặt tệp lib của bạn (* .so) là chạy ứng dụng của bạn và in nativeLibraryDir bằng cách sử dụng: System.out.println (getApplicationContext (). GetApplicationInfo (). NativeLibraryDir), tên của thư mục cũng sẽ cung cấp cho bạn ABI.
David Rauca

20

Trong gradle, sau khi sao chép tất cả các thư mục tệp vào libs/

jniLibs.srcDirs = ['libs']

Thêm dòng trên vào sourceSetstrong build.gradletệp đã hoạt động. Không có gì khác hoạt động.


2
"sourceSets" có nằm trong tệp build.gradle không?
Ashana.Jackol

12

Bạn đang sử dụng gradle? Nếu vậy hãy đưa .sotệp vào<project>/src/main/jniLibs/armeabi/

Tôi hy vọng nó sẽ giúp.


không, tôi không sử dụng gradle, Tôi đang sử dụng eclipse + maven
user842225

12

Trong trường hợp của tôi, tôi phải loại trừ các nguồn biên dịch theo gradle và đặt đường dẫn libs

android {

    ...
    sourceSets {
        ...
        main.jni.srcDirs = []
        main.jniLibs.srcDirs = ['libs']
    }
....

điều này cũng giải quyết được cho tôi và tôi đã thêm các tệp armeabi vào thư mục armeabi-v7a và x86 nhưng tôi không chắc liệu nó có cần thiết hay không.
dokam_scotland

8

Lý do cho lỗi này là do có sự không khớp về ABI giữa ứng dụng của bạn và thư viện gốc mà bạn đã liên kết. Nói cách khác, ứng dụng của bạn và của bạn .sođang nhắm mục tiêu ABI khác nhau.

nếu bạn tạo ứng dụng của mình bằng các mẫu Android Studio mới nhất, thì ứng dụng đó có thể nhắm mục tiêu arm64-v8anhưng bạn .socó thể nhắm mục tiêu armeabi-v7achẳng hạn.

Có 2 cách để giải quyết vấn đề này:

  1. xây dựng các thư viện gốc của bạn cho mỗi ABI mà ứng dụng của bạn hỗ trợ.
  2. thay đổi ứng dụng của bạn để nhắm mục tiêu ABI cũ hơn mà bạn đã .soxây dựng.

Lựa chọn 2 là bẩn nhưng tôi nghĩ bạn có thể quan tâm nhiều hơn đến:

thay đổi ứng dụng của bạn build.gradle

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

điều gì xảy ra nếu ứng dụng của tôi ở trong nhật thực? Sự cố này xảy ra khi tôi di chuyển cùng một ứng dụng tùy chỉnh từ 6 sang 9.
Shadow

6

Để tham khảo, tôi có thông báo lỗi này và giải pháp là khi bạn chỉ định thư viện, bạn bỏ sót 'lib' ở phía trước và '.so' ở cuối.

Vì vậy, nếu bạn có tệp libmyfablib.so, bạn cần gọi:

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

Sau khi xem apk, cài đặt / gỡ cài đặt và thử tất cả các loại giải pháp phức tạp, tôi không thể thấy vấn đề đơn giản đang ở ngay trước mặt mình!


Điều đó là vậy đó. Vì một số lý do không xác định, Android không cài đặt các thư viện có tên tệp không bắt đầu bằng 'lib', ngay cả khi chúng có trong gói. Đi tìm ...
George Y.

Tôi có thể kiểm tra điều này ở đâu trong dự án? Ý tôi là nơi tôi có thể tìm thấy dòng này System.loadLibrarytrong mã
aleksandrbel

Cảm ơn. Điều này đã giúp!
Riskhan

5

Đây là bản cập nhật Android 8.

Trong phiên bản Android cũ hơn, với thư viện chia sẻ gốc LoadLibrary (ví dụ: để truy cập qua JNI), tôi đã cố định mã gốc của mình để lặp qua một loạt đường dẫn thư mục tiềm năng cho thư mục lib, dựa trên các thuật toán cài đặt / nâng cấp apk khác nhau:

/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

Cách tiếp cận này là hokey và sẽ không hoạt động đối với Android 8; từ https://developer.android.com/about/versions/oreo/android-8.0-changes.html, bạn sẽ thấy rằng như một phần của các thay đổi "Bảo mật", bạn hiện cần sử dụng sourceDir:

"Bạn không còn có thể cho rằng APK nằm trong các thư mục có tên kết thúc bằng -1 hoặc -2. Các ứng dụng nên sử dụng sourceDir để lấy thư mục và không dựa trực tiếp vào định dạng thư mục."

Đính chính, sourceDir không phải là cách để tìm các thư viện được chia sẻ gốc của bạn; sử dụng một cái gì đó như thế nào. Đã thử nghiệm cho 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;
}

4

Cố gắng gọi thư viện của bạn sau PREBUILT_SHARED_LIBRARYphần bao gồm :

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

Cập nhật:

Nếu bạn sẽ sử dụng thư viện này trong Java, bạn cần biên dịch nó thành thư viện chia sẻ

LOCAL_PATH := $(call my-dir)

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

Và bạn cần triển khai thư viện trong /vendor/libthư mục.


Chỉ trong phần cuối của phần.
Alex

2

Bạn chỉ có thể thay đổi ABI để sử dụng các bản dựng cũ hơn:

defaultConfig {
    ...

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

Bạn cũng nên sử dụng NDK không dùng nữa bằng cách thêm dòng này vào gradle.properties:

android.useDeprecatedNdk=true

0

vui lòng thêm tất cả 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

1
Bạn có thể vui lòng nói rõ hơn về những gì cần làm?
EFrank

Tệp .so trong dự án của bạn. Bạn nên hỗ trợ arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64. Khi tôi làm điều đó, nó hoạt động tốt.
Lion 耿

-1

Theo kinh nghiệm của tôi, trong thiết bị di động armeabi-v7a, khi cả thư mục armeabi và armeabi-v7a đều có trong apk, các tệp .so trong thư mục armeabi sẽ không được liên kết, mặc dù các tệp .so trong armeabi SẼ được liên kết trong cùng di động armeabi-v7a, nếu armeabi-v7a không có mặt.


-1

trên thực tế, bạn không thể chỉ đặt một tệp .so vào /libs/armeabi/và tải nó bằngSystem.loadLibrary . Bạn cần tạo tệp Android.mk và khai báo một mô-đun dựng sẵn nơi bạn chỉ định tệp .so của mình làm nguồn.

Để làm như vậy, hãy đặt tệp .so của bạn và tệp Android.mk vào jnithư mục. Android.mk của bạn sẽ trông giống như sau:

LOCAL_PATH := $(call my-dir)

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

Nguồn: Tài liệu Android NDK về bản dựng sẵn

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.