簡體   English   中英

通過JNI / NDK使用預編譯的C共享庫

[英]Using pre-compiled C shared library with JNI/NDK

我目前正在開發一個具有C ++集成的小型演示Android應用程序。 我正在使用Android Studio 3.1.4,並且我的項目具有NDK支持(開發環境為Windows 10 )。

該應用程序的目的是從存儲(sdcard)加載共享庫(.so)文件,然后動態鏈接該庫。

這是我創建該應用程序所經歷的步驟:

1.在Cygwin上使用GCC生成共享庫

這些是我用來編譯庫的文件

“sumLib.h”:

#define _MYLIB_H_
#define MAX_INPUT 25
extern double sum(double x, double y);

“sumLib.c”:

#include <stdlib.h>
#include "sumLib.h"
double sum(double x, double y) {
    return x + y;
}

然后,我使用以下這些命令來生成庫文件:

gcc -shared -o sumLib.o -c sumLib.c
gcc -shared -o sumLib.so sumLib.o

2.在Android客戶端上加載庫文件

我已將文件傳輸到Android模擬器的sd卡中(使用設備文件瀏覽器) (/storage/emulated/0/Download/sumLib.so)

這是讀取該庫並將其與符號“ sum”鏈接的c ++代碼:

extern "C" {
JNIEXPORT jdouble JNICALL
Java_com_example_user_demo_MainActivity_sum(JNIEnv *env, jobject obj, jdouble n1, jdouble n2) {
    void *handle;
    double (*sum)(double, double);
    char *error;

    char fname[PATH_MAX];
    strcpy(fname, internalPath); //internal path is a predefined string that points to the Download folder in the SD card
    strcat(fname, "/sumLib.so");

    handle = dlopen(fname, RTLD_LAZY);
    if (!handle) {
        __android_log_write(ANDROID_LOG_ERROR, "Creating handle", "Error creating handle");
        exit(EXIT_FAILURE);
    }

    dlerror();

    *(void **) (&sum) = dlsym(handle, "sum");

    if ((error = dlerror()) != NULL) {
        __android_log_write(ANDROID_LOG_ERROR, "Error linking symbol", error);
        exit(EXIT_FAILURE);
    }

    double result = (*sum)(n1, n2);
    dlclose(handle);
    return result;
}
}

這是調用c ++函數的Java代碼:

public native double sum(double n1, double n2);

// function was called after the click of a button
public void onClickTestBtn(View v) {

    // set up the text view to display the result
    TextView tv = findViewById(R.id.sample_text);
    double result = sum(134.3, 11.3);
    tv.setText(String.format("Result is %f", result));
}

理論上,這應該調用c ++定義的“ sum”函數 ,並使用該庫查找符號“ sum”並執行操作,但是dlopen無法從文件創建句柄,因此會不斷拋出錯誤。

這是錯誤消息:

"dlopen failed: library \"/storage/emulated/0/Download/sumLib.so\" needed or dlopened by \"/data/app/com.example.user.demo-fzNLN7tBu86LCNun1yLQlg==/lib/x86_64/libnative-lib.so\" is not accessible for the namespace \"classloader-namespace\""

我在錯誤消息中的文件地址末尾注意到,他們添加了一個正斜杠而不是反斜杠,這有什么影響嗎? 另外,我不確定錯誤消息的后半部分是什么意思。

我認為,在Cygwin GCC可能在不同的體系結構來編譯什么Android模擬器上運行,但經過我檢查了文件,我發現它編譯為64位 (根據這個 ,我發現PE d +從閱讀文件)。 對於仿真器,我正在運行x86_64映像(API 27),並在調用后將其打印出x86_64

String arch = System.getProperty("os.arch");
Log.d("Debug", String.format("system arch is %s", arch));

我還檢查了是否可以從c ++環境訪問該文件,所以我使用了

FILE *f = fopen(fname, "r");

並且在調試器中,它沒有指向null,而是指向了0x00007227dd414018 ,確切地說,是否可以確定它找到了文件並可以正確讀取文件是安全的?

我不確定如何解決dlopen給我的錯誤,至於庫文件的完整性和正確性,我已使用以下C代碼對其進行了測試:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int agrc, char **agrv) {
    void *handle;
    double (*sum)(double, double);
    char *error;

    handle = dlopen("./sumLib.so", RTLD_LAZY);
    if (!handle) {
        printf("error opening file");
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE);
    }
    dlerror();

    *(void **) (&sum) = dlsym(handle, "sum");

    if ((error = dlerror()) != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(EXIT_FAILURE);
    }
    printf("Sum is %f\n", (*sum)(13.3, 113.4));
    dlclose(handle);
    exit(EXIT_SUCCESS);
    return 0;
}

它能夠正確打開庫,鏈接符號並返回正確的結果。

我做錯了什么使dlopen拋出該錯誤?

嘗試為目標SDK 23或更低版本構建您的應用,並在模擬器API 24或更低版本上運行它。 我相信您看到的故障是最近安全措施的體現。 請參閱Android 7.0行為更改

從Android 7.0開始,系統會阻止應用程序動態鏈接非NDK庫。

使用主機gcc編譯器構建sumLib.so也無濟於事 即使生成的庫針對相同的x86_64 ABI,Android上的運行時環境也完全不同。 請使用NDK工具鏈。 如果願意,可以使用獨立工具鏈代替ndk-buildCMake腳本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM