Статьи

Android Gradle: добавление нативных .so зависимостей

Фон 

Несколько месяцев назад я написал базу данных Key-Value для Android под названием  SnappyDB  на основе Google  LevelDB . Поскольку он использует собственный код C ++, сгенерированный пакет содержит ( .so ) двоичные библиотеки libs вместе с Jars. 

Распространение через репозиторий Maven не является проблемой (Как только вы пропустите хлопоты по процессу публикации :),  maven-android-plugiпоможет вам включить общие библиотеки. Соглашение о зависимостях Maven позволяет вам указать тип ABI (разные архитектуры ЦП) и формат библиотеки (в нашем случае, очевидно, .so), которую вы хотите разрешить, используя классификатор: 

Пример: разрешение общей библиотеки ARM для SnappyDB:

<dependency>
  <groupId>com.snappydb</groupId>
  <artifactId>snappydb-native</artifactId>
  <version>0.2.0</version>
  <classifier>armeabi</classifier>
  <type>so</type>
</dependency>

Этот подход прекрасно работает, если вы используете Maven & Eclipse ADT в качестве системы сборки, пока не уступите вызову сирены Gradle! 

Android Studio & Gradle 

Плагин Android Gradle, корректно обрабатывает все зависимости Jars с помощью репозиториев Maven (среди прочего …) 

пример : объявление зависимости внутри build.gradle

dependencies {
     classpath 'commons-io:commons-io:2.4'
}

но он сталкивается с трудностями, когда дело касается нативных зависимостей, по сравнению с Maven, вы не можете написать что-то вроде этого:

dependencies {
       classpath 'com.snappydb:snappydb-native:2.+:arm-v7a'
}

Это связано с тем, что поддержка NDK все еще находится в стадии разработки с плагином Android. (как с Android Studio).

 ¹ на самом деле, технически говоря, вы можете, но Gradle просто проигнорирует эти собственные файлы, так как не знает, что с ними делать.

jniLibs на помощь!

В    версии 0.7.2 плагина для Android Google представил новую папку « jniLibs » для исходных наборов. Это означает, что теперь вы можете добавить свои предварительно созданные файлы .so в эту папку, а плагин Android позаботится об упаковке этих собственных библиотек в ваш APK.

.
├── AndroidManifest.xml
└── jniLibs
    ├── armeabi
    │   └── libsnappydb-native.so
    ├── armeabi-v7a
    │   └── libsnappydb-native.so
    ├── mips
    │   └── libsnappydb-native.so
    └── x86
        └── libsnappydb-native.so

Эта функция великолепна, но разработчику все равно нужно вручную загружать и копировать свои предварительно созданные файлы .so , что не очень хорошо, особенно если вы используете сервер непрерывной интеграции, например, Jenkins или Travis. 

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

Итак, вы получите картину. Там должен быть лучший способ.

Встречайте android-native-зависимости

android-native-dependencies — это плагин Gradle, который я написал для автоматизации процесса разрешения, загрузки и копирования собственных зависимостей в папку jniLibs , поэтому плагин Android может автоматически включать их в сборку APK. 

Плагин использует тот же репозиторий, который объявлен для разрешения обычных зависимостей (jar). Вот пример:

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:0.10.+'
    classpath 'com.nabilhachicha:android-native-dependencies:0.1'
  }
}

apply plugin: 'android'
apply plugin: 'android-native-dependencies'

native_dependencies {
    artifact 'com.snappydb:snappydb-native:0.2+:armeabi'
    artifact 'com.snappydb:snappydb-native:0.2+:x86'
}

dependencies {
    //regular Jar dependencies ...
}

условность

DSL артефакта следует соглашению об именовании для артефактов Maven. Таким образом, вы можете использовать один из следующих синтаксисов: 

  • сокращенная группа: имя: версия [: классификатор]
//adding x86 classifier will resolve only intel's (.so) lib
native_dependencies {
    artifact 'com.snappydb:snappydb-native:0.2+:x86'
}

//omit the classifier will resolve all supported architectures
native_dependencies {
    artifact 'com.snappydb:snappydb-native:0.2+'
}
  • нотация в стиле карты
//adding x86 classifier will resolve only intel's (.so) lib
native_dependencies {
    artifact group: 'com.snappydb', name: 'snappydb-native', version: '0.2+', classifier: 'x86'
}

//omit the classifier will resolve all supported architectures
native_dependencies {
    artifact group: 'com.snappydb', name: 'snappydb-native', version: '0.2+'
}

В обоих обозначениях классификатор не обязателен. это означает, что, если он не указан , плагин пытается разрешить артефакты для всех архитектур: armeabi , armeabi-v7a , x86 и mips

Вывод

Пока мы не получим полную поддержку NDK в плагине Android Gradle, использование android-native-dependencies может помочь вам создать свой CI и автоматизировать повторяющуюся задачу с нативными зависимостями. Пожалуйста, попробуйте и отправьте свой отзыв на  @nabilhachicha  . 

Другой замечательный плагин Gradle, который я рекомендую — это  android-sdk-manager  ( Джейк Уортон ), который помогает загружать и управлять вашим Android SDK.