Android NDK/JNI: создание общей библиотеки, которая зависит от других разделяемых библиотек
Я пишу приложение для Android, которое хочет сделать вызовы JNI в общую библиотеку, созданную с использованием NDK. Фокус в том, что это общие функции вызовов библиотеки, предоставляемые ДРУГИМИ разделяемыми библиотеками. Другие общие библиотеки - это библиотеки C, которые были скомпилированы в другом месте.
Вот что я пробовал:
Моя среда:
Я работаю в Eclipse. Я добавил собственную поддержку и имею библиотеку jni. В этой библиотеке у меня есть мой код и каталог \lib, где я скопировал другие .so файлы.
Попытка # 1 Android.mk: просто сообщите, где находятся
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib1
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib2
include $(BUILD_SHARED_LIBRARY)
Это просто отлично, но когда я пытаюсь запустить, я получаю ошибки, указывающие на то, что dlopen (libnative_lib) не удалось, потому что он не мог загрузить libsupport_lib1.
Придя сюда, я нашел это:
Может ли общая библиотека вызывать другую разделяемую библиотеку?
в котором говорилось, что мне нужно вызвать библиотеку загрузки для всех необходимых библиотек. Отлично!
Попытка # 2 Открытие каждой библиотеки сначала
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
Опять же, это просто отлично, но когда я запускаю, я получаю новую ошибку:
не удалось загрузить libsupport_lib1. findLibrary возвращается null.
Теперь мы куда-то добираемся. Он не должен загружать библиотеки в цель.
Попытка # 3 Копирование .so файлов в проект /libs/armeabi
Не работает. Когда Eclipse создает, он удаляет файлы, которые я туда запустил.
Попытка # 4 Создание нового модуля для каждой библиотеки
Итак, я нашел это:
Android NDK: ссылка с использованием предварительно скомпилированной статической библиотеки
Это о статических библиотеках, но, возможно, у меня такая же проблема. Суть в том, что мне нужно объявить модуль для каждой библиотеки. Итак, мой новый Android.mk выглядит так:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib1.so
include $(BUILD_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib2.so
include $(BUILD_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib1
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib2
include $(BUILD_SHARED_LIBRARY)
Это строит! Еще лучше, теперь армеаби имеет sos! Даже ЛУЧШЕ Я получаю следующие сообщения, когда я пытаюсь запустить его (говоря мне, что support_lib1 и 2 были открыты LoadLibrary:
Попытка загрузить lib/data/app-lib/com.example.tst/libsupport_lib1.so
добавлен общий файл lib/data/app-lib/com.example.tst/libsupport_lib1.so
нет JNI_OnLoad найдено в /data/app -lib/com.example.tst/libsupport_lib1.so, пропущен init
но затем...
dlopen не удалось: не удалось найти символ func_that_exists_in_libsupport_lib.so, на который ссылается libnative_lib.so
Изменить: Попытка 5: Использовать PREBUILT_SHARED_LIBRARY
Итак, я нашел это:
Как я могу связать предварительно созданную общую библиотеку с проектом NDK для Android?
который, кажется, именно то, что я прошу. Их ответ кажется "не использовать" build_shared_library ", но вместо этого" используйте PREBUILT_SHARED_LIBRARY
Хорошо, пусть попробует.
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib2.so
include $(PREBUILT_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2
include $(BUILD_SHARED_LIBRARY)
Построить... не удается! Теперь сборка жалуется на отсутствие символов.
Изменить: Попытка 6: Сгладить все
Итак, я вернулся к документации по предварительным загрузкам в NDK. В нем говорится:
Каждая предварительно построенная библиотека должна быть объявлена как отдельный независимый модуль для системы сборки. Вот тривиальный пример, где мы предполагаем, что файл "libfoo.so" находится в том же каталоге, что и Android.mk ниже:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
include $(PREBUILT_SHARED_LIBRARY)
Обратите внимание, что для объявления такого модуля вам действительно нужно только следующее:
Дайте модулю имя (здесь "foo-prebuilt" ). Это не обязательно должно соответствовать имени самой предварительно созданной библиотеки.
Назначьте LOCAL_SRC_FILES путь к предварительно созданной библиотеке, которую вы предоставляете. Как обычно, путь относится к вашему LOCAL_PATH.
Включите PREBUILT_SHARED_LIBRARY, а не BUILD_SHARED_LIBRARY, если вы предоставляете общую библиотеку. Для статических используйте PREBUILT_STATIC_LIBRARY.
Предварительно построенный модуль ничего не создает. Однако копия вашей предварительно созданной общей библиотеки будет скопирована в $PROJECT/obj/local, а другая будет скопирована и разделена на $PROJECT/libs/.
Итак, попробуйте сгладить все, чтобы соответствовать тривиальному примеру. Я скопировал свои библиотеки из их удобной папки /lib и поместил их в jni-root. Затем я сделал следующее:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := support_lib1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := support_lib2.so
include $(PREBUILT_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2
include $(BUILD_SHARED_LIBRARY)
и... такая же ошибка. Более того, я определенно НЕ вижу, что файлы библиотек копируются в $PROJECT/obj/local.
sooooo.... теперь что?
Ответы
Ответ 1
Ваша проблема связана с соглашением об именах. NDK и Android настаивают на том, чтобы имена разделяемых библиотек всегда начинались с lib. В противном случае библиотеки не будут связаны должным образом, а не скопированы в папку libs/armeabi
правильно и не будут установлены на устройство (скопировано в каталог /data/data/package/lib
правильно).
Если вы переименуете support_lib1.so
в libsupport_1.so
и support_lib2.so
в libsupport_2.so
, и поместите эти два файла в каталог jni/lib
, то ваша попытка №5 будет работать с незначительными изменениями:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := lib/libsupport_1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := lib/libsupport_2.so
include $(PREBUILT_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2
include $(BUILD_SHARED_LIBRARY)
Кстати, я не думаю, что вам нужно это -L$(SYSROOT)/../usr/lib
.
PS Не забудьте также обновить сторону Java:
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
Ответ 2
Не уверен, что это именно то, где вы находитесь, но вот что я знаю об этих вещах.
- Сделайте каждую предварительно созданную библиотеку своей отдельной Makefile. Несколько целей в Android.mk, как правило, становятся неуклюжими. Сад.
- Включите каждый файл make с помощью
$(call import-add-path)
и $(call import-module)
- Экспортируйте как можно больше из предварительно созданных файлов make, используя семейство переменных
LOCAL_EXPORT_
.
Предварительная общая библиотека Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := my_module_name
MY_LIBRARY_NAME := shared_library_name
### export include path
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
### path to library
LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/lib$(MY_LIBRARY_NAME).so
### export dependency on the library
LOCAL_EXPORT_LDLIBS := -L$(LOCAL_PATH)/libs/$(TARGET_ARCH_ABI)/
LOCAL_EXPORT_LDLIBS += -l$(MY_LIBRARY_NAME)
include $(PREBUILT_SHARED_LIBRARY)
Это предполагает, что предварительно созданные библиотеки хранятся в структуре dir, подобной этой
+ SharedProjectFolderName
+--- Android.mk
+--- include/
+-+- libs/$(TARGET_ARCH_ABI)/
|- libshared_library_name.so
Если вы не создаете несколько ABI, я думаю, вы можете оставить этот бит
Проект Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := my_jni_module
## source files here, etc...
### define dependency on the other library
LOCAL_SHARED_LIBRARIES := my_module_name
include $(BUILD_SHARED_LIBRARY)
$(call import-add-path,$(LOCAL_PATH)/path/to/myLibraries/)
$(call import-module,SharedProjectFolderName)
$(call import-module,AnotherSharedProject)
Я рекомендую вам поместить все разделяемые библиотеки в одну папку. Когда вы говорите $(call import-module,SharedProjectFolderName)
, он ищет папку, содержащую Android.mk
вдоль пути поиска, который вы ему сказали (import-add-path
)
Кстати, вы, вероятно, не должны указывать LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib
. Он должен найти собственные библиотеки из NDK сам по себе. Добавление большего количества путей компоновщика, вероятно, путает его. Правильный способ - экспортировать пути компоновщика в виде флагов из подмодулей.
ТАКЖЕ, вы можете использовать ndk-build V=1
, чтобы получить тонну информации о том, почему она не может найти пути и т.д.
Ответ 3
Параметр -L предоставляет компоновщику путь к каталогу, в котором нужно искать библиотеки. Параметр -l дает компоновщику имя файла библиотеки для ссылки. Имена файлов библиотек должны начинаться с "lib". Ваши библиотеки должны быть названы libsupport_lib1.so и libsupport_lib2.so. Если вы это сделаете, то это, вероятно, то, что вы должны сделать (заменив попытку №1):
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog -lsupport_lib1 -lsupport_lib2
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib
Линкером будет префикс имени библиотеки, которое вы укажете с помощью -l с "lib", и суффикс с ".so". (Почему у вас есть -L $(SYSROOT)/../usr/lib?)
Я считаю, что попытки # 1 и # 2 потерпели неудачу, потому что вы не связывали свои библиотеки с вашим исполняемым файлом - они не упоминаются в опции -l. Кстати, вы можете проверить это сами. Разархивируйте файл .apk и посмотрите в каталоге lib и подкаталогах. Ваши файлы .so там?
Глядя на ошибку:
but then... dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so
Можете ли вы предоставить все сообщение? dlopen() загружает и связывает библиотеки в текущий процесс.