System.loadLibrary (…) n'a pas pu trouver de bibliothèque native dans mon cas

91

Je souhaite utiliser une bibliothèque native existante à partir d' un autre projet Android, je viens donc de copier la bibliothèque intégrée NDK ( libcalculate.so ) dans mon nouveau projet Android. Dans mon nouveau projet Android, j'ai créé un dossier libs/armeabi/et mis libcalculate.so là-bas. Il n'y a pas de dossier jni /. Mon appareil de test a une architecture ARM.

Dans mon code java, je charge la bibliothèque par:

  static{
    System.loadLibrary("calculate");
  }

Lorsque je lance mon nouveau projet Android, j'ai une erreur:

java.lang.UnsatisfiedLinkError:  ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libcalculate.so"

Donc, comme le dit l'erreur, la bibliothèque native copiée n'est pas dans / verdor / lib ou / system / lib, comment résoudre ce problème dans mon cas?

(J'ai décompressé le paquet apk, sous lib / il y a libcalculate.so)

==== MISE À JOUR =====

J'ai également essayé de créer un dossier jni / sous la racine du projet et d'ajouter un fichier Android.mk sous jni /. Le contenu d'Android.mk est:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

Ensuite, sous la racine du projet, j'ai exécuté ndk-build. Après cela, les répertoires armeabi / et armeabi-v7a / sont générés par ndk-build (avec libcalculate.so dans le dossier).

Ensuite, je lance mon maven construire le projet avec succès. Dans le package apk final, il y a:

lib/armeabi/libcalculate.so
lib/armeabi-v7a/libcalculate.so

Mais lorsque j'exécute mon application, la même erreur renvoie:

java.lang.UnsatisfiedLinkError:  ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libcalculate.so"
user842225
la source
3
Vous mettez la bibliothèque directement sous libs/? Vous devez probablement créer un sous-répertoire par ABI cible que vous souhaitez prendre en charge (armeabi, armeabi-v7a, x86, mips, etc.) et placer le fichier .so approprié dans chaque sous-répertoire (c'est-à-dire que le fichier .so construit pour armeabi entre libs/armeabi/, etc).
Michael
@Michael, je viens de manquer ça dans mon post, je l'ai mis sous libs / armeabi /
user842225
Vérifiez que libcalculate.so est effectivement capté par le processus de packaging - essayez par exemple unzip -l package.apk, ou renommez l'apk en .zip et ouvrez-le avec une application. S'il n'y est pas, quelque chose ne va pas avec l'empaquetage (votre IDE a-t-il remarqué que le dossier était là, avez-vous besoin de rafraîchir le projet?).
mstorsjo
@mstorsjo, j'ai décompressé le paquet apk, sous lib / il y a libcalculate.so
user842225
1
vous n'avez pas besoin d'avoir un Android.mk ou des fichiers liés à la compilation. Il suffit de mettre les fichiers so dans leurs sous-répertoires correspondants à jniLibs comme ici: github.com/Ph1b/MaterialAudiobookPlayer/tree/master/audiobook
Paul Woitaschek

Réponses:

174

Pour enraciner la cause (et peut-être résoudre votre problème en même temps), voici ce que vous pouvez faire:

  1. Supprimez le dossier jni et tous les fichiers .mk . Vous n'avez pas besoin de ceux-ci ni du NDK si vous ne compilez rien.

  2. Copiez votre libcalculate.sofichier à l'intérieur <project>/libs/(armeabi|armeabi-v7a|x86|...). Lorsque vous utilisez Android Studio, c'est <project>/app/src/main/jniLibs/(armeabi|armeabi-v7a|x86|...), mais je vois que vous utilisez eclipse.

  3. Construisez votre APK et ouvrez-le sous forme de fichier zip , pour vérifier que votre libcalculate.sofichier se trouve dans lib / (armeabi | armeabi-v7a | x86 | ...) .

  4. Supprimez et installez votre application

  5. Exécuter les packages de package dumpsys | grep yourpackagename pour obtenir le nativeLibraryPath ou legacyNativeLibraryDir de votre application.

  6. Exécutez ls sur le nativeLibraryPath que vous aviez ou sur legacyNativeLibraryDir / armeabi , pour vérifier si votre libcalculate.so est bien là.

  7. S'il est là, vérifiez s'il n'a pas été modifié à partir de votre fichier libcalculate.so d' origine : est-il compilé avec la bonne architecture, contient-il les symboles attendus, y a-t-il des dépendances manquantes. Vous pouvez analyser libcalculate.so en utilisant readelf.

Afin de vérifier l'étape 5-7, vous pouvez utiliser mon application au lieu des lignes de commande et lire vous-même: Native Libs Monitor

PS: Il est facile de ne pas savoir où les fichiers .so doivent être placés ou générés par défaut, voici un résumé:

  • libs / CPU_ABI dans un projet eclipse

  • jniLibs / CPU_ABI dans un projet Android Studio

  • jni / CPU_ABI dans un AAR

  • lib / CPU_ABI dans l'APK final

  • dans le nativeLibraryPath de l'application sur un appareil <5.0, et dans le legacyNativeLibraryDir / CPU_ARCH de l' application sur un appareil> = 5.0.

CPU_ABI est l'un des éléments suivants: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64 . En fonction des architectures que vous ciblez et pour lesquelles vos bibliothèques ont été compilées.

Notez également que les bibliothèques ne sont pas mélangées entre les répertoires CPU_ABI: vous avez besoin de l'ensemble complet de ce que vous utilisez, une bibliothèque qui se trouve dans le dossier armeabi ne sera pas installée sur un périphérique armeabi-v7a s'il y a des bibliothèques à l'intérieur de l' armeabi -v7a dossier de l'APK.

ph0b
la source
3
Super homme, merci! J'utilise Android Studio et mes builds jni étaient copiés dans des bibliothèques au lieu de jniLibs.
Luis
4
Cette dernière note sur le jeu complet était cruciale pour moi. C'était mon problème, merci!
Ben Trengrove
Pour la 7section: voulez-vous dire que le .so pourrait être modifié à partir de l'APK après son installation sur l'appareil? Si tel est le cas, y aurait-il une chance que le système ruine le fichier .so?
jayatubi
5
Merveilleux! Dans mon cas très étrange, j'utilisais un ensemble de bibliothèques tierces (OpenCV - dans le dossier armeabi ) et ces bibliothèques ont cessé de se charger lorsque j'ai ajouté une bibliothèque tierce différente via Gradle. Il s'avère que la deuxième bibliothèque ne prend pas en charge ARMv5 ou 6, et en l'incluant, mes bibliothèques OpenCV sont devenues invisibles (même si elles étaient réellement là ). Votre point sur l'ensemble complet m'a donné l'indice - renommer le dossier armeabi et l'appeler armeabi-v7a a résolu le problème (puisque je ne supporte plus ARM 5 ou 6 ...). Mauvais problème !!
Mete du
1
Une autre façon de trouver où placer votre fichier lib (* .so) est d'exécuter votre application et d'imprimer le nativeLibraryDir en utilisant: System.out.println (getApplicationContext (). GetApplicationInfo (). NativeLibraryDir), le nom du répertoire sera également vous fournir l'ABI.
David Rauca
19

Dans gradle, après avoir copié tous les dossiers de fichiers dans libs/

jniLibs.srcDirs = ['libs']

L'ajout de la ligne ci-dessus sourceSetsdans le build.gradlefichier a fonctionné. Rien d'autre n'a fonctionné du tout.

Mukund Muralikrishnan
la source
2
"sourceSets" est-il situé dans le fichier build.gradle?
Ashana.Jackol
12

Utilisez-vous gradle? Si c'est le cas, mettez le .sofichier dans<project>/src/main/jniLibs/armeabi/

J'espère que cela aide.

Assaf Gamliel
la source
non, je n'utilise pas gradle, j'utilise eclipse +
maven
12

Dans mon cas, je dois exclure la compilation des sources par gradle et définir le chemin des libs

android {

    ...
    sourceSets {
        ...
        main.jni.srcDirs = []
        main.jniLibs.srcDirs = ['libs']
    }
....
Dawid Drozd
la source
cela a également été résolu pour moi et j'ai ajouté les fichiers d'armeabi dans les dossiers armeabi-v7a et x86 mais je ne suis pas sûr si c'était nécessaire.
dokam_scotland
8

La raison de cette erreur est qu'il existe une incompatibilité de l'ABI entre votre application et la bibliothèque native avec laquelle vous vous êtes lié. Un autre mot, votre application et votre .sociblent différents ABI.

si vous créez votre application à l'aide des derniers modèles Android Studio, elle cible probablement le arm64-v8amais votre .sociblage peut-être armeabi-v7apar exemple.

Il existe 2 façons de résoudre ce problème:

  1. créez vos bibliothèques natives pour chaque ABI pris en charge par votre application.
  2. modifiez votre application pour cibler l'ancienne ABI sur laquelle vous avez .soconstruit.

Le choix 2 est sale mais je pense que vous êtes probablement plus intéressé par:

changer votre application build.gradle

android {
    defaultConfig {
        ...
        ndk {
            abiFilters 'armeabi-v7a'
        }
   }
}
wdanxna
la source
Et si mon application était en éclipse? Ce problème survient lorsque je migre la même application personnalisée de 6 à 9.
Shadow
6

Pour référence, j'avais ce message d'erreur et la solution était que lorsque vous spécifiez la bibliothèque, vous manquiez le «lib» sur le devant et le «.so» de la fin.

Donc, si vous avez un fichier libmyfablib.so, vous devez appeler:

   System.loadLibrary("myfablib"); // this loads the file 'libmyfablib.so' 

Après avoir regardé dans l'apk, installé / désinstallé et essayé toutes sortes de solutions complexes, je ne pouvais pas voir le problème simple qui se trouvait juste devant mon visage!

Andy Krouwel
la source
C'était ça. Pour une raison inconnue, Android n'installe pas les bibliothèques dont le nom de fichier ne commence pas par «lib», même si elles sont présentes dans le package. Allez comprendre ...
George Y.19
Où puis-je vérifier cela dans le projet? Je veux dire où puis-je trouver cette ligne System.loadLibrarydans le code
aleksandrbel
Merci. Cela a aidé!
Riskhan
5

Ceci est une mise à jour Android 8.

Dans la version antérieure d'Android, aux bibliothèques partagées natives de LoadLibrary (pour un accès via JNI par exemple), j'ai câblé mon code natif pour itérer à travers une gamme de chemins de répertoire potentiels pour le dossier lib, en fonction des divers algorithmes d'installation / de mise à niveau d'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

Cette approche est hokey et ne fonctionnera pas pour Android 8; à partir de https://developer.android.com/about/versions/oreo/android-8.0-changes.html, vous verrez que dans le cadre de leurs modifications de «sécurité», vous devez maintenant utiliser sourceDir:

"Vous ne pouvez plus supposer que les fichiers APK résident dans des répertoires dont les noms se terminent par -1 ou -2. Les applications doivent utiliser sourceDir pour obtenir le répertoire et ne pas dépendre directement du format du répertoire."

Correction, sourceDir n'est pas le moyen de trouver vos bibliothèques partagées natives; utilisez quelque chose comme. Testé pour 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;
}
AlgèbreHiver
la source
4

Essayez d'appeler votre bibliothèque après la PREBUILT_SHARED_LIBRARYsection d' inclusion :

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

Mettre à jour:

Si vous utilisez cette bibliothèque en Java, vous devez la compiler en tant que bibliothèque partagée

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(BUILD_SHARED_LIBRARY)

Et vous devez déployer la bibliothèque dans le /vendor/librépertoire.

Alex
la source
Seulement à la fin de la section.
Alex
2

Vous pouvez simplement changer ABI pour utiliser des versions plus anciennes:

defaultConfig {
    ...

    ndk {
        abiFilters 'armeabi-v7a'
    }
    ...
}

Vous devez également utiliser NDK obsolète en ajoutant cette ligne à gradle.properties:

android.useDeprecatedNdk=true
rezam
la source
0

veuillez ajouter tout le support

app / build.gradle

ndk {
        moduleName "serial_port"
        ldLibs "log", "z", "m"
        abiFilters "arm64-v8a","armeabi", "armeabi-v7a", "x86","x86_64","mips","mips64"
}

app \ src \ jni \ Application.mk

APP_ABI := arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64
Lion 耿
la source
1
Pourriez-vous être plus précis sur ce qu'il faut faire?
EFrank du
Le fichier .so dans votre projet. Vous devriez prendre en charge arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64. Quand je faisais ça, ça marchait bien.
Lion 耿
-1

D'après mon expérience, dans un mobile armeabi-v7a, lorsque les répertoires armeabi et armeabi-v7a sont présents dans l'apk, les fichiers .so dans le répertoire armeabi ne seront pas liés, bien que les fichiers .so dans armeabi seront liés dans le même mobile armeabi-v7a, si armeabi-v7a n'est pas présent.

loopscn
la source
-1

en fait, vous ne pouvez pas simplement mettre un fichier .so dans le /libs/armeabi/et le charger avec System.loadLibrary. Vous devez créer un fichier Android.mk et déclarer un module prédéfini dans lequel vous spécifiez votre fichier .so comme source.

Pour ce faire, placez votre fichier .so et le fichier Android.mk dans le jnidossier. Votre Android.mk devrait ressembler à ceci:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

Source: documentation Android NDK sur le préconstruit

Yannshu
la source