Android NDK import-модуль / повторное использование кода - PullRequest
12 голосов
/ 15 июня 2011

Утро!

Я создал небольшой проект NDK, который позволяет динамическую сериализацию объектов между Java и C ++ через JNI. Логика работает так:

Бин -> JavaCInterface.Java -> JavaCInterface.cpp -> JavaCInterface.java -> Бин

Проблема в том, что я хочу использовать эту функциональность в других проектах. Я отделил тестовый код от проекта и создал проект «Тестер». Проект тестера отправляет объект Java на C ++, который затем возвращает его обратно на уровень Java.

Я думал, что связывание будет довольно простым - («Простой» с точки зрения NDK / JNI обычно является днем ​​разочарования) Я добавил проект JNIBridge в качестве исходного проекта и включил следующие строки в Android.mk:

NDK_MODULE_PATH=.../JNIBridge/jni/"

JNIBridge / JNI / JavaCInterface / Android.mk:

...
include $(BUILD_STATIC_LIBRARY)

JNITester / JNI / Android.mk:

...
include $(BUILD_SHARED_LIBRARY)
$(call import-module, JavaCInterface)

Это все отлично работает. Файлы C ++, основанные на заголовках из модуля JavaCInterface, работают нормально. Также классы Java могут с радостью использовать интерфейсы из проекта JNIBridge. Все ссылки счастливы.

К сожалению, JavaCInterface.java, который содержит вызовы собственных методов, не может видеть метод JNI, расположенный в статической библиотеке. (Логически они находятся в одном проекте, но оба они импортируются в проект, где вы хотите использовать их с помощью вышеуказанного механизма).

Мои текущие решения следующие. Я надеюсь, что кто-то может предложить что-то, что сохранит модульный характер того, чего я пытаюсь достичь:


Мое текущее решение будет включать файлы cpp JavaCInterface в вызывающий проект, например, так:

LOCAL_SRC_FILES := FunctionTable.cpp $(PATH_TO_SHARED_PROJECT)/JavaCInterface.cpp

Но я бы предпочел не делать этого, так как это привело бы к необходимости обновления каждого зависимого проекта, если бы я изменил архитектуру JavaCInterface.


Я мог бы создать новый набор сигнатур методов JNI в каждом локальном проекте, который затем связывался с импортированными модулями. Опять же, это слишком тесно связывает реализации.

1 Ответ

15 голосов
/ 16 июня 2011

После большого количества пота крови и слез я понял это.

  • Android JNI загружает свой двоичный файл только с SHARED_LIBRARY.
  • JNI будет пытаться связать собственные вызовы с соответствующими сигнатурами / заглушками методов из загруженной общей библиотеки (она не будет заглядывать внутрь связанных общих библиотек).
  • С помощью этих методов вы можете создать статическую библиотеку и встроить ее в общую библиотеку, используемую вашим приложением.

Вы можете создать свою статическую библиотеку в исходном проекте, используя следующий код в файле Andriod.xml:

include $(CLEAR_VARS)
LOCAL_CFLAGS    := -O0
LOCAL_MODULE    := LibraryToBeUsedInsideSharedLib
LOCAL_SRC_FILES := ...
include $(BUILD_STATIC_LIBRARY) // This builds a "Static Object" here:
                                // /Project/obj/local/armeabi/libLibraryToBeUsedInsideSharedLib.a

include $(CLEAR_VARS)
LOCAL_MODULE       := LibraryCalledFromJava
LOCAL_SRC_FILES    := ...
LOCAL_STATIC_LIBRARIES := LibraryToBeUsedInsideSharedLib
include $(BUILD_SHARED_LIBRARY)

LOCAL_STATIC_LIBRARIES включает в себя статическую библиотеку в вашей общей библиотеке. В вашем Java-коде вы теперь можете вызвать это:

System.loadLibrary("LibraryCalledFromJava");

Вы должны иметь возможность вызывать любые собственные методы, расположенные внутри библиотеки LibraryToBeUsedInsideSharedLib, из любой точки вашего Java-кода.

Вы можете экспортировать файл libLibraryToBeUsedInsideSharedLib.a и использовать его в других проектах, добавив его в файл Android.xml внешнего проекта:

include $(CLEAR_VARS)
LOCAL_MODULE            := LibraryToBeUsedInsideSharedLib
LOCAL_LDLIBS            := -llog/
LOCAL_SRC_FILES         := $(MY_PREBUILT_LIB_DIR)/libLibraryToBeUsedInsideSharedLib.a
include $(PREBUILT_STATIC_LIBRARY)
...