将预编译的 C 共享库与 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模拟器的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 模拟器运行的架构不同的架构上进行编译,但在我检查文件后,我发现它是为x64编译的(据此,我从读取文件中找到了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, "%sn", dlerror());
exit(EXIT_FAILURE);
}
dlerror();
*(void **) (&sum) = dlsym(handle, "sum");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%sn", error);
exit(EXIT_FAILURE);
}
printf("Sum is %fn", (*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也无济于事。尽管生成的库面向相同的 ABIx86_64,但 Android 上的运行时环境完全不同。请使用 NDK 工具链。如果您愿意,可以使用独立的工具链而不是ndk-buildCMake脚本。

最新更新