web-dev-qa-db-fra.com

Android Gradle Ajout d'une bibliothèque statique

Dans l'ancien ndk Android traditionnel, nous spécifierons la bibliothèque statique à associer dans le fichier Android.mk.

Android.mk 

PLATFORM_PREFIX := /opt/Android-ext/
LOCAL_PATH := $(PLATFORM_PREFIX)/lib
include $(CLEAR_VARS)
LOCAL_MODULE := library
LOCAL_SRC_FILES := library.a
include $(PREBUILT_STATIC_LIBRARY)

LOCAL_STATIC_LIBRARIES := Android_native_app_glue library

Voici ma question

Je suis un peu confus lors du passage au plug-in expérimental Gradle pour NDK. Partagez vos idées sur la façon de lier la bibliothèque statique dans le fichier build.gradle de l'application.

J'avais suivi la dernière documentation expérimentale du plug-in Gradle donnée ici.

15
Sankar ganesh

Jetez un oeil à cet échantillon .

  1. Indiquez au compilateur où se trouvent les en-têtes (dans Android.ndk{}):

    CFlags += "-I${file("path/to/headers")}".toString() cppFlags += CFlags

  2. Indiquez au lieur où se trouve le fichier .a (dans Android.ndk {} ou où Définissant les saveurs - assurez-vous d'ajouter abiFilter - par exemple abiFilters += "armeabi-v7")

    ldFlags += "-L${file(path/to/library.a)}".toString() ldLibs += ["nameOfLibrary"]

    Notez que le nom de la bibliothèque par convention est la chaîne après "Lib" dans le nom de fichier .a. Par exemple, pour un fichier nommé libNative.a, vous devez ajouter ldLibs + = ["native"] à dégradé.

  3. Créez un nouveau module et utilisez apply plugin: 'Java' pour appliquer le plug-in Java. Dans le fichier build.gradle, écrivez le code nécessaire pour obtenir et placez le fichier .a dans le répertoire approprié (où il sera récupéré à partir du module qui l’utilise). N'oubliez pas d'ajouter une dépendance dans le module à l'aide de la bibliothèque (compile project(':libraryModule') dans dependencies{}) et de l'inclure dans le projet dans le fichier settings.gradle avec include ':libraryModule'. Si vous souhaitez placer le module dans un dossier spécifié par votre dossier (par exemple, où se trouve actuellement votre fichier Android.mk), ajoutez simplement project(':libraryModule').projectDir = new File(settingsDir, 'path/to/module').

Ça devrait le faire.

10
Nedko

Les réponses ci-dessus permettent de contourner l'intégration insuffisante antérieure de NDK par gradle. Cette réponse illustre l’intégration de nouveaux grades au NDK.

Jetez un oeil à cette proposition sample écrite contre Gradle 2.9 et le plugin Android 0.6.0-alpha1. Contrairement à la manière dont la question est posée, cette réponse contient un projet distinct pour la bibliothèque. Cette fonctionnalité peut être explorée pour permettre à Gradle de construire cette bibliothèque avant qu'elle ne soit utilisée par le projet d'application. Les autres réponses reposent sur l'hypothèse que la bibliothèque avait déjà été construite.

Le: secondlib com.Android.model.application construit libsecondlib.so (chargé en code Java avec System.loadLibrary ("secondlib"). Le nom 'secondlib' est mal nommé. J'aime le voir comme un "wrapper" .so pour toutes les autres bibliothèques natives. lié pour une utilisation par l'application.

Cette bibliothèque partagée est liée statiquement à firstlib.a telle que construite par: firstlib com.Android.model.native.

Les en-têtes sont exportés de la: firstlib vers des projets dépendants (: secondlib dans cet exemple) conformément à la clause exportedHeaders. De cette façon, les projets dépendants savent comment établir un lien avec le .so/.a. Ceci remplace la syntaxe CFlags + = "- I/chemin/vers/en-têtes" dans une réponse antérieure.

: secondlib est lié à: firstlib de manière statique conformément à la clause suivante:

Android.sources {
    main {
         jni {
             dependencies {
                 project ":firstlib" buildType "debug" linkage "static"
             }
         }
         // TODO(proppy): show jniLibs dependencies on .so
    }
}

Comme le montre le commentaire, l'exemple est incomplet. La syntaxe de finalisation est indiquée dans la section «Dépendances NDK» de la documentation du plug-in Android experimental . Dans ce document, la syntaxe permettant de lier statiquement plutôt que dynamiquement devrait être claire.

Il existe actuellement quelques inconvénients (par exemple, le type de construction de la dépendance montré ci-dessus est par défaut 'debug' et non le type de construction en cours de construction).

EDIT: Voici un exemple en cours de la nouvelle syntaxe des dépendances dans app/build.gradle extraite de l’un de mes projets:

  Android.sources {
    main {
      jni {
        //for exportedHeaders
        dependencies { project ":libfoo" linkage "shared" }
      }
      jniLibs {
        //Where the swig wrapped library .so is. I use swig to create code to interface with libfoo.so within :app
        source { srcDirs 'libs' }
        //for file in $(model.repositories.libs.libfoo)
        dependencies { library "libfoo" }
      }
    }
  }

  repositories {
    libs(PrebuiltLibraries) {
      libevdev {
        //headers already available from our libfoo project via exportedHeaders
        //headers.srcDir "../libfoo/src/main/jni/"
        binaries.withType(SharedLibraryBinary) {
          sharedLibraryFile = file("../libfoo/build/intermediates/binaries/debug/lib/${targetPlatform.getName()}/libfoo.so")
        }
      }
    }
  }
3
Andrew Smart

Conceptuellement, apk est un zip de manifeste, de fichiers natifs et de bibliothèque de classes . Donc, si vous copiez la bibliothèque statique pour la sortie, cela fonctionnera . Donc, vous devriez utiliser la tâche de copie graduellement pour rendre ces bibliothèques intégrées à la sortie. . Je viens d'utiliser ci-dessous pour mes fichiers so et cela a fonctionné. 

task copyNativeLibs2h(type: Copy) {
    from(new File(getProjectDir(), 'libs')) { include '**/*.so' }
    into new File(buildDir, 'native-libs')
}
tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn copyNativeLibs2h }
1
Ashish Rawat

Vous ne pouvez pas construire une bibliothèque statique avec gradle, même avec le plugin expérimental. Vous pouvez utiliser une bibliothèque prédéfinie ou la construire avec ndk-build et la lier à l'objet partagé avec le plugin Gradle.

Voici un exemple:

Supposons que nous ayons cette structure de répertoire:

  • projet (build.gradle, gradle.properties, etc.)
    • jni_static (Application.mk, Android.mk, fichiers cpp pour la bibliothèque statique)
    • app (build.gradle, src/main, etc.)
      • jni_shared (fichiers cpp pour la bibliothèque partagée)

Et voici les éléments pertinents de project/app/build.gradle :

// look for NDK directory

import org.Apache.tools.ant.taskdefs.condition.Os
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkBuild = properties.getProperty('ndk.dir') + '/ndk-build'
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    ndkBuild += '.cmd'
}

apply plugin: 'com.Android.model.application'

… Dépendances, modèle {compileOptions, etc.

// static lib is built with ndk-build

def LOCAL_MODULE = "static"
def appAbi = "armeabi-v7a"
def ndkOut = "build/intermediates/$LOCAL_MODULE"
def staticLibPath = "$ndkOut/local/$appAbi/lib${LOCAL_MODULE}.a"

// To guarantee that the intermediates shared library is always refreshed,
// we delete it in gradle task rmSO.

task rmSO(type: Delete) {
    delete 'build/intermediates/binaries/debug/lib/armeabi-v7a', 'libshared.so'
    delete 'build/intermediates/binaries/debug/obj/armeabi-v7a', 'libshared.so'
}

// in file jni/Android.mk there is a section for LOCAL_MODULE=static
// which builds the static library

task buildStaticLib(type: Exec, description: 'Compile Static lib via NDK') {
    commandLine "$ndkBuild", "$staticLibPath", "NDK_APPLICATION_MK=../jni_static/Application.mk",
            "NDK_PROJECT_PATH=../jni_static", "NDK_OUT=$ndkOut"
    dependsOn rmSO
}

task cleanNative(type: Exec, description: 'Clean JNI object files') {
    commandLine "$ndkBuild", "clean", "NDK_APPLICATION_MK=../jni_static/Application.mk",
            "NDK_PROJECT_PATH=../jni_static", "NDK_OUT=$ndkOut"
}
clean.dependsOn cleanNative

tasks.all {
    task ->

// link of the shared library depends on build of the static lib
        if (task.name.startsWith('link')) {
            task.dependsOn buildStaticLib
        }

// before build, make sure the intermediate so is not stuck there
        if (task.name.startsWith('package')) {
            task.dependsOn rmSO
        }
}

// build the wrapper shared lib around the static lib using the experimental plugin

model {
    Android.ndk {
        moduleName = "shared"
        cppFlags += "-std=c++11"
        ldFlags += "$staticLibPath".toString()
        ldLibs += "log"

        stl = "gnustl_static"
        abiFilters += "$appAbi".toString()
    }

    Android.sources {
        main.jni.source {
            srcDirs = ["jni_shared"]
        }
    }
}

Important: les sources de la bibliothèque partagée doivent se trouver dans un répertoire séparé, afin que les sources de la bibliothèque statique ne s'y trouvent pas. Cela parce que le plug-in expérimental ne peut pas exclure certains fichiers sous srcDirs.

0
Alex Cohn