android - studio - que es ndk
System.loadLibrary(...) no pudo encontrar una biblioteca nativa en mi caso (12)
Quiero usar una biblioteca nativa existente de
otro
proyecto de Android, por lo que acabo de copiar la biblioteca construida NDK (
libcalculate.so
) a mi nuevo proyecto de Android.
En mi nuevo proyecto de Android, creé una carpeta
libs/armeabi/
y puse
libcalculate.so
allí.
No
hay
jni / carpeta.
Mi dispositivo de prueba tiene arquitectura ARM.
En mi código Java, cargo la biblioteca de la siguiente manera:
static{
System.loadLibrary("calculate");
}
Cuando ejecuto mi nuevo proyecto de Android, recibí un error:
java.lang.UnsatisfiedLinkError: ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn''t find "libcalculate.so"
Entonces, como dice el error, la biblioteca nativa copiada no está en / verdor / lib o / system / lib, ¿cómo resolver este problema en mi caso?
(Descomprimí el paquete apk, en lib / hay libcalculate.so)
==== ACTUALIZACIÓN =====
También intenté crear una carpeta jni / en la raíz del proyecto y agregar un archivo Android.mk en jni /. El contenido de Android.mk es:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)
Luego, bajo la raíz del proyecto, ejecuté ndk-build. Después de eso, los directorios armeabi / y armeabi-v7a / son generados por ndk-build (con libcalculate.so dentro de la carpeta).
Luego ejecuto mi maven para construir el proyecto con éxito. En el paquete apk final, hay:
lib/armeabi/libcalculate.so
lib/armeabi-v7a/libcalculate.so
Pero cuando ejecuto mi aplicación, aparece el mismo error:
java.lang.UnsatisfiedLinkError: ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn''t find "libcalculate.so"
¿Estás usando gradle?
Si es así, coloque el archivo
.so
en
<project>/src/main/jniLibs/armeabi/
Espero que ayude.
A raíz de la causa (y tal vez resolver su problema al mismo tiempo), esto es lo que puede hacer:
-
Elimine la carpeta jni y todos los archivos .mk . No necesita estos ni el NDK si no está compilando nada.
-
Copie su archivo
libcalculate.so
dentro de<project>/libs/(armeabi|armeabi-v7a|x86|...)
. Cuando se usa Android Studio, es<project>/app/src/main/jniLibs/(armeabi|armeabi-v7a|x86|...)
, pero veo que estás usando eclipse. -
Cree su APK y ábralo como un archivo zip , para verificar que su archivo
libcalculate.so
esté dentro de lib / (armeabi | armeabi-v7a | x86 | ...) . -
Elimina e instala tu aplicación
-
Ejecute paquetes de paquetes dumpsys | grep yourpackagename para obtener nativeLibraryPath o legacyNativeLibraryDir de su aplicación.
-
Ejecute ls en nativeLibraryPath que tenía o en legacyNativeLibraryDir / armeabi , para verificar si su libcalculate.so está realmente allí.
-
Si está allí, verifique si no ha sido alterado de su archivo libcalculate.so original: está compilado contra la arquitectura correcta, contiene los símbolos esperados, faltan dependencias. Puede analizar libcalculate.so usando readelf.
Para verificar el paso 5-7, puede usar mi aplicación en lugar de líneas de comando y readelf: Monitor de Libs nativas
PD: Es fácil confundirse sobre dónde deben colocarse o generarse los archivos .so de manera predeterminada, aquí hay un resumen:
-
libs / CPU_ABI dentro de un proyecto eclipse
-
jniLibs / CPU_ABI dentro de un proyecto de Android Studio
-
jni / CPU_ABI dentro de un AAR
-
lib / CPU_ABI dentro del APK final
-
dentro de nativeLibraryPath de la aplicación en un dispositivo <5.0, y dentro de legacyNativeLibraryDir / CPU_ARCH de la aplicación en un dispositivo> = 5.0.
Donde CPU_ABI es cualquiera de: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64 . Dependiendo de las arquitecturas a las que se dirija y para las que se hayan compilado sus bibliotecas.
Tenga en cuenta también que las bibliotecas no se mezclan entre los directorios CPU_ABI: necesita el conjunto completo de lo que está utilizando, una biblioteca que está dentro de la carpeta armeabi no se instalará en un dispositivo armeabi-v7a si hay alguna biblioteca dentro del armeabi -v7a carpeta de la APK.
Como referencia, recibí este mensaje de error y la solución fue que cuando especificas la biblioteca, pierdes la ''lib'' del frente y el ''.so'' del final.
Entonces, si tiene un archivo libmyfablib.so, debe llamar a:
System.loadLibrary("myfablib"); // this loads the file ''libmyfablib.so''
Después de buscar en el apk, instalar / desinstalar y probar todo tipo de soluciones complejas, ¡no pude ver el simple problema que estaba frente a mí!
En gradle, después de copiar todas las carpetas de archivos a
libs/
jniLibs.srcDirs = [''libs'']
Agregar la línea anterior a
sourceSets
en el archivo
build.gradle
funcionó.
Nada más funcionó en absoluto.
En mi caso, debo excluir las fuentes de compilación por gradle y establecer la ruta de acceso de las bibliotecas
android {
...
sourceSets {
...
main.jni.srcDirs = []
main.jniLibs.srcDirs = [''libs'']
}
....
En mi experiencia, en un móvil armeabi-v7a, cuando los directorios armeabi y armeabi-v7a están presentes en el apk, los archivos .so en el directorio armeabi no estarán vinculados, aunque los archivos .so en armeabi se vincularán en el mismo armeabi-v7a móvil, si armeabi-v7a no está presente.
Esta es una actualización de Android 8.
En versiones anteriores de Android, a las bibliotecas compartidas nativas de LoadLibrary (para acceder a través de JNI, por ejemplo), conecté mi código nativo para iterar a través de un rango de posibles rutas de directorio para la carpeta lib, en función de los diversos algoritmos de instalación / actualización de apk:
/data/data/<PackageName>/lib
/data/app-lib/<PackageName>-1/lib
/data/app-lib/<PackageName>-2/lib
/data/app/<PackageName>-1/lib
/data/app/<PackageName>-2/lib
Este enfoque es divertido y no funcionará para Android 8; de https://developer.android.com/about/versions/oreo/android-8.0-changes.html verá que, como parte de sus cambios de "Seguridad", ahora necesita usar sourceDir:
"Ya no puede suponer que las APK residen en directorios cuyos nombres terminan en -1 o -2. Las aplicaciones deben usar sourceDir para obtener el directorio y no depender directamente del formato del directorio".
Corrección, sourceDir no es la forma de encontrar sus bibliotecas compartidas nativas; usa algo como Probado para Android 4.4.4 -> 8.0
// Return Full path to the directory where native JNI libraries are stored.
private static String getNativeLibraryDir(Context context) {
ApplicationInfo appInfo = context.getApplicationInfo();
return appInfo.nativeLibraryDir;
}
Intente llamar a su biblioteca después de incluir la sección
PREBUILT_SHARED_LIBRARY
:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)
#...
LOCAL_SHARED_LIBRARIES += libcalculate
Actualizar:
Si va a utilizar esta biblioteca en Java, debe compilarla como biblioteca compartida.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(BUILD_SHARED_LIBRARY)
Y necesita implementar la biblioteca en el directorio
/vendor/lib
.
La razón de este error es porque hay una falta de coincidencia de la ABI entre su aplicación y la biblioteca nativa con la que se vinculó.
En otras palabras, su aplicación y su
.so
apuntan a diferentes ABI.
si crea su aplicación utilizando las últimas plantillas de Android Studio, probablemente
arm64-v8a
dirigida a
arm64-v8a
pero su
.so
puede estar dirigida a
armeabi-v7a
por ejemplo.
Hay 2 formas de resolver este problema:
- cree sus bibliotecas nativas para cada ABI que admita su aplicación.
-
cambie su aplicación para apuntar a una ABI más antigua con la que su
.so
construida.
La opción 2 está sucia, pero creo que probablemente te interese más:
cambia el
build.gradle
tu aplicación
android {
defaultConfig {
...
ndk {
abiFilters ''armeabi-v7a''
}
}
}
Simplemente puede cambiar ABI para usar compilaciones anteriores:
ndk {
moduleName "serial_port"
ldLibs "log", "z", "m"
abiFilters "arm64-v8a","armeabi", "armeabi-v7a", "x86","x86_64","mips","mips64"
}
También debe usar NDK en desuso agregando esta línea a
gradle.properties
:
APP_ABI := arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64
en realidad, no puede simplemente colocar un archivo .so en
/libs/armeabi/
y cargarlo con
System.loadLibrary
.
Debe crear un archivo Android.mk y declarar un módulo preconstruido donde especifique su archivo .so como fuente.
Para hacerlo, coloque su archivo .so y el archivo Android.mk en la carpeta
jni
.
Tu Android.mk debería verse así:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libcalculate LOCAL_SRC_FILES := libcalculate.so include $(PREBUILT_SHARED_LIBRARY)
por favor agregue todo el soporte
app / build.gradle
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)
app / src / jni / Application.mk
APP_ABI := arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64