В настоящее время я занимаюсь разработкой небольшого демо-приложения для Android с интеграцией C ++. Я использую Android Studio 3.1.4, и мой проект поддерживает NDK (среда разработки Windows 10 ).
Цель приложения - загрузить файл общей библиотеки (.so) из хранилища (sdcard), а затем динамически связать библиотеку.
Вот шаги, которые я прошел, чтобы создать это приложение:
1. Использование GCC в Cygwin для создания общей библиотеки
Это файлы, которые я использовал для компиляции библиотеки
"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-карту эмулятора Android (используя Device File Explored) (/ storage / emulated / 0 / Download / sumLib.so)
Вот код C ++, который читает библиотеку и связывает ее с символом "sum" :
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;
}
}
Вот код Java, который вызывает функцию c ++:
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 ++, и использовать библиотеку для поиска символа «сумма» и выполнения операции, но 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\""
Я заметил в конце адреса файла в сообщении об ошибке, что они добавили прямую косую черту вместо обратной косой черты. Также я не уверен, что означает вторая половина сообщения об ошибке.
Я считал, что GCC на Cygwin может компилироваться на архитектуре, отличной от той, на которой работает эмулятор 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");
и в отладчике он не указывал на ноль, он указывал на что-то, 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 выдает эту ошибку?