Android Gradle Добавление статической библиотеки

В старом традиционном андроиде ndk мы укажем статическую библиотеку, которая будет связана в файле 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

Вот мой вопрос

Я немного запутался при переключении на Gradle экспериментальный плагин для NDK. Поделитесь своими идеями о том, как связать статическую библиотеку в файле приложения build.gradle.

Я следил за последней Gradle экспериментальной плагиновой документацией приведенной здесь.

Ответы

Ответ 1

Посмотрите на этот образец.

  • Сообщите компилятору, где находятся заголовки (в android.ndk{}):

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

  • Сообщите компоновщику, где находится файл .a(в android.ndk {} или где определение вкусов - обязательно добавьте abiFilter - например abiFilters += "armeabi-v7")

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

    Обратите внимание, что имя библиотеки по соглашению является строкой после "lib" в имени файла .a. Например, для файла с именем libNative.a вы должны добавить ldLibs + = [ "native" ] в gradle.

  • Создайте новый модуль и используйте apply plugin: 'java' для применения java-плагина. В build.gradle напишите необходимый код, чтобы получить и поместить файл .a в соответствующий каталог (где вы его получите из своего модуля, который его использует). Не забудьте добавить зависимость в модуле с помощью библиотеки (compile project(':libraryModule') в dependencies{}) и включить ее в проект в файле settings.gradle с помощью include ':libraryModule'. Если вы хотите поместить модуль в указанную вами папку (например, где в настоящий момент находится ваш файл Android.mk), просто добавьте project(':libraryModule').projectDir = new File(settingsDir, 'path/to/module').

Это должно сделать это.

Ответ 2

Вышеупомянутые ответы работают вокруг gradle до недостаточной интеграции NDK. Этот ответ иллюстрирует новую интеграцию gradle с NDK.

Взгляните на предложенный sample, написанный против gradle 2.9 и плагин android 0.6.0-alpha1. В отличие от того, как задан вопрос, этот ответ содержит отдельный проект для библиотеки. Эта функциональность может быть исследована, чтобы позволить gradle создавать эту библиотеку до ее использования в проекте приложения. Другие ответы основываются на предположении, что библиотека уже построена.

The: secondlib com.android.model.application строит libsecondlib.so(загружается в Java-код с помощью System.loadLibrary( "secondlib" ). Имя 'secondlib' плохо названо. Мне нравится думать об этом как .so "wrapper" для всех других родных библиотек, связанных для использования приложением.

Эта общая библиотека статически связана с firstlib.a как построенная по: firstlib com.android.model.native.

Заголовки экспортируются из: firstlib в любые зависимые проекты (: secondlib в этом примере) в соответствии с предложением exportedHeaders. Таким образом, зависимые проекты знают, как связываться с .so/.a. Это заменяет синтаксис CFlags + = "- I/path/to/headers" в предыдущем ответе.

: ссылки secondlib против: firstlib статически в соответствии со следующим предложением:

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

Как показано в комментарии, пример неполный. Финальный синтаксис показан в разделе "Зависимости NDK" экспериментальной документации по подключению к Android. Внутри этого документа синтаксис для статической ссылки вместо динамической ссылки должен быть ясным.

В настоящее время существуют некоторые недостатки (например, buildType зависимости, показанной выше, по умолчанию "debug", а не текущий построенный тип buildType).

EDIT: вот образец выполнения нового синтаксиса зависимостей в приложении /build.gradle, извлеченный из одного из моих проектов:

  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

Концептуально apk - это zip манифеста, собственные файлы и библиотека классов. Поэтому, если вы скопируете статическую библиотеку для вывода, она будет работать. Поэтому в gradle вы должны использовать задачу копирования и сделать эту библиотеку частью процесса вывода. Я просто использовал ниже для своих файлов, и это сработало.

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

Ответ 4

Вы не можете создать статическую библиотеку с gradle, даже с экспериментальным плагином. Вы можете использовать предварительно созданную библиотеку или создать ее с помощью ndk-build и связать ее с общим объектом с помощью плагина gradle.

Вот пример:

Предположим, что мы имеем эту структуру каталогов:

  • project (build.gradle, gradle.properties и т.д.)
    • jni_static (Application.mk, Android.mk, файлы cpp для статического lib)
    • app (build.gradle, src/main и т.д.)
      • jni_shared (файлы cpp для общей библиотеки)

И вот соответствующие фрагменты 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'

... зависимости, model {compileOptions и т.д.

// 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"]
        }
    }
}

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