ndk - jni android
Android NDK/JNI: Creación de una biblioteca compartida que depende de otras bibliotecas compartidas (3)
Estoy escribiendo una aplicación de Android que desea realizar llamadas JNI en una biblioteca compartida incorporada utilizando el NDK. El truco es que esta biblioteca compartida llama a las funciones proporcionadas por OTRAS bibliotecas compartidas. Las otras bibliotecas compartidas son bibliotecas C que se han compilado en otros lugares.
Esto es lo que he intentado:
Mi entorno: estoy trabajando en Eclipse. He añadido soporte nativo y tengo una biblioteca jni. En esa biblioteca tengo mi código y un directorio / lib donde he copiado mis otros archivos .so.
Intento # 1 con Android.mk: solo le digo dónde están las librerías
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)
Esto funciona bien, pero cuando intento ejecutarlo obtengo errores que indican que dlopen (libnative_lib) falló porque no se pudo cargar libsupport_lib1.
Viniendo aquí encontré esto:
¿Puede la biblioteca compartida llamar a otra biblioteca compartida?
que decía que necesitaba llamar a la biblioteca de carga en todas las bibliotecas necesarias. ¡Genial!
Intento # 2 Abriendo cada biblioteca primero
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
Nuevamente, esto se desarrolla bien, sin embargo, cuando ejecuto recibo un nuevo error:
no se pudo cargar libsupport_lib1. findLibrary devolvió un valor nulo.
Ahora estamos llegando a alguna parte. No debe estar cargando las bibliotecas sobre el destino.
Intento # 3 Copiando archivos .so en project / libs / armeabi
No funciono Cuando Eclipse construye, borra los archivos que coloqué.
Intento # 4 Creando un nuevo módulo para cada biblioteca
Entonces encontré esto:
NDK de Android: enlace utilizando una biblioteca estática precompilada
Se trata de bibliotecas estáticas, pero tal vez tenga un problema similar. Lo esencial es que necesito declarar un módulo para cada biblioteca. Así que mi nuevo Android.mk se ve así:
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)
¡Esto construye! Aún mejor, el armeabi tiene el sos ahora! Incluso MEJOR recibo los siguientes mensajes cuando intento ejecutarlo (me indica que LoadLibrary abrió support_lib1 y 2:
Intentando cargar lib /data/app-lib/com.example.tst/libsupport_lib1.so se agregó lib lib /data/app-lib/com.example.tst/libsupport_lib1.so no se encontró JNI_OnLoad en / data / app-lib / com.example.tst / libsupport_lib1.so, saltando init
pero luego ... falló dlopen: no se pudo localizar el símbolo func_that_exists_in_libsupport_lib.so al que hace referencia libnative_lib.so
Edición: intento 5: usar PREBUILT_SHARED_LIBRARY
Así que encontré esto: ¿Cómo puedo vincular la biblioteca compartida precompilada al proyecto NDK de Android?
que parece ser exactamente lo que estoy preguntando. Su respuesta parece ser ''no use'' build_shared_library ''pero en su lugar'' use PREBUILT_SHARED_LIBRARY
Está bien, vamos a intentarlo.
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)
Construir ... falla! La compilación se queja ahora de que faltan símbolos.
Edit: Intento 6: Aplanar todo
Así que volví a la documentación previa al proyecto en el NDK. Dice:
Cada biblioteca precompilada debe declararse como un único módulo independiente para el sistema de compilación. Aquí hay un ejemplo trivial donde asumimos que el archivo "libfoo.so" está ubicado en el mismo directorio que el Android.mk a continuación:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
include $(PREBUILT_SHARED_LIBRARY)
Tenga en cuenta que, para declarar dicho módulo, solo necesita lo siguiente:
Dale un nombre al módulo (aquí ''foo-prebuilt''). No es necesario que esto se corresponda con el nombre de la biblioteca precompilada.
Asigne a LOCAL_SRC_FILES la ruta a la biblioteca precompilada que está proporcionando. Como de costumbre, la ruta es relativa a su LOCAL_PATH.
Incluya PREBUILT_SHARED_LIBRARY, en lugar de BUILD_SHARED_LIBRARY, si está proporcionando una biblioteca compartida. Para los estáticos, use PREBUILT_STATIC_LIBRARY. Un módulo precompilado no construye nada. Sin embargo, una copia de su biblioteca compartida precompilada se copiará en $ PROJECT / obj / local, y otra se copiará y se eliminará en $ PROJECT / libs /.
Así que intentemos aplanar todo para que coincida con el ejemplo trivial. Copié mis bibliotecas de su acogedora carpeta / lib y las puse en la raíz jni. Entonces hice esto:
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)
y ... el mismo error. Además, definitivamente NO veo que los archivos de la biblioteca se copien a $ PROJECT / obj / local.
muuuuy .... ahora que?
La opción -L le da al enlazador una ruta de directorio en la que buscar las bibliotecas. La opción -l le da al enlazador un nombre de archivo de biblioteca para vincular. Los nombres de archivo de biblioteca deben comenzar con "lib". Las bibliotecas deben llamarse libsupport_lib1.so y libsupport_lib2.so. Si haces eso, entonces esto es probablemente lo que debes hacer (reemplazando el intento # 1):
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog -lsupport_lib1 -lsupport_lib2
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib
El enlazador prefijará el nombre de la biblioteca que especifique utilizando -l con "lib" y lo colocará con ".so". (¿Por qué tienes -L $ (SYSROOT) /../ usr / lib?)
Creo que los intentos # 1 y # 2 fallaron porque no vinculó sus bibliotecas a su ejecutable, no se mencionan en la opción -l. Por cierto, puedes verificar esto tú mismo. Descomprima el archivo .apk y busque en el directorio lib y los subdirectorios. ¿Están sus archivos .so ahí?
Mirando el error:
but then... dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so
¿Puede proporcionar el mensaje completo? dlopen () carga y enlaza las bibliotecas en el proceso en ejecución.
No estoy seguro si esto es exactamente donde estás, pero esto es lo que sé sobre este tipo de cosas.
- Haga que cada biblioteca pre-compilada tenga su propio Makefile separado. Múltiples objetivos en Android.mk tiende a obtener wonky. Triste.
- Incluya cada archivo make utilizando
$(call import-add-path)
y$(call import-module)
- Exporte todo lo que pueda de los archivos make
LOCAL_EXPORT_
, utilizando la familia de variablesLOCAL_EXPORT_
.
Biblioteca compartida preconfigurada 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)
Esto es asumiendo que las bibliotecas precompiladas viven en una estructura de directorio como esta
+ SharedProjectFolderName
+--- Android.mk
+--- include/
+-+- libs/$(TARGET_ARCH_ABI)/
|- libshared_library_name.so
Si no estás construyendo para múltiples ABI, supongo que puedes dejar ese bit fuera
El Android.mk del proyecto
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)
Te recomiendo que pongas todas las bibliotecas compartidas en una carpeta. Cuando dices $(call import-module,SharedProjectFolderName)
busca una carpeta que contenga un Android.mk
largo de la ruta de búsqueda que le dijiste ( import-add-path
)
Por cierto, probablemente no deberías especificar LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib
. Debería encontrar las librerías adecuadas de NDK por sí mismo. Agregar más rutas de enlace probablemente lo confundirá. La forma correcta es exportar las rutas del vinculador como indicadores desde los submódulos.
TAMBIÉN, puede usar ndk-build V=1
para obtener un montón de información sobre por qué no puede encontrar rutas, etc.
Su problema es con la convención de nomenclatura. NDK y Android insisten en que los nombres de las bibliotecas compartidas comiencen siempre con lib . De lo contrario, las bibliotecas no se vincularán correctamente, no se copiarán en la carpeta libs/armeabi
y no se instalarán en el dispositivo (se copiarán en el directorio /data/data/package/lib
correctamente).
Si cambia el nombre de support_lib1.so
a libsupport_1.so
y support_lib2.so
a libsupport_2.so
, y coloca estos dos archivos en el directorio jni/lib
, su intento # 5 funcionará con un cambio menor:
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)
Por cierto, no creo que necesites este -L$(SYSROOT)/../usr/lib
.
PS No te olvides de actualizar el lado de Java, también:
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}