Интегрируем .so файлы в APK
Если вы используете Android Studio, то для интеграции нативных библиотек в приложение раньше было необходимо применение различных сложных способов, включая maven и пакеты .aar/.jar … Хорошая новость состоит в том, что теперь этого уже не требуется.
Вам только требуется положить .so библиотеки в каталог jniLibs в поддиректории, названные соответственно каждой поддерживаемой ABI (x86, mips, armeabi-v7a, armeabi) – и всё! Теперь все файлы .so будут интегрированы в APK во время сборки:
Если название папки jniLibs вас не устраивает, вы можете задать другое расположение в build.gradle:
android { ... sourceSets.main { jniLibs.srcDir 'src/main/libs' } }
Строим один APK на архитектуру и добиваемся успеха!
Построить один APK на архитектуру очень просто с использованием свойства abiFilter.
По умолчанию ndk.abiFilter(s) имеет значение all. Это свойство оказывает влияние на интеграцию .so файлов, а также на обращения к ndk-build (мы поговорим об этом в конце поста).
Давайте внесем некоторые архитектурные особенности (конфигурации) в build.gradle:
android{ ... productFlavors { x86 { ndk { abiFilter "x86" } } mips { ndk { abiFilter "mips" } } armv7 { ndk { abiFilter "armeabi-v7a" } } arm { ndk { abiFilter "armeabi" } } fat } }
А затем синхронизируем проект с файлами gradle:
Теперь вы можете наслаждаться новыми возможностями, выбирая желаемые варианты сборки:
Каждый из этих вариантов даст вам APK для выбранной архитектуры:
Полный (Release|Debug) APK будет все еще содержать все библиотеки, как и стандартный пакет, упомянутый в начале этого поста.
Но не прекращайте читать на этом месте! Архитектурно-зависимые APK удобны при разработке, но если вы хотите залить несколько из них в Google Play Store, вам необходимо установить различный versionCode для каждого. Сделать это с помощью новейшей системы сборки очень просто.
Автоматически устанавливаем различные коды версий для ABI-зависимых APK
Свойство android.defaultConfig.versionCode отвечает за versionCode для вашего приложения. По умолчанию оно установлено в -1 и, если вы не измените это значение, будет использован versionCode, указанный в файле AndroidManifest.xml.
Поскольку мы хотим динамически изменять наш versionCode, сначала необходимо указать его внутри build.gradle:
android { ... defaultConfig{ versionName "1.1.0" versionCode 110 } }
Однако все еще возможно хранить эту переменную в AndroidManifest.xml, если вы получаете ее «вручную» перед изменением:
import java.util.regex.Pattern android { ... defaultConfig{ versionCode getVersionCodeFromManifest() } ... } def getVersionCodeFromManifest() { def manifestFile = file(android.sourceSets.main.manifest.srcFile) def pattern = Pattern.compile("versionCode=\"(\\d+)\"") def matcher = pattern.matcher(manifestFile.getText()) matcher.find() return Integer.parseInt(matcher.group(1)) }
Теперь вы можете использовать versionCode с различными модификаторами:
android { ... productFlavors { x86 { versionCode Integer.parseInt("6" + defaultConfig.versionCode) ndk { abiFilter "x86" } } mips { versionCode Integer.parseInt("4" + defaultConfig.versionCode) ndk { abiFilter "mips" } } armv7 { versionCode Integer.parseInt("2" + defaultConfig.versionCode) ndk { abiFilter "armeabi-v7a" } } arm { versionCode Integer.parseInt("1" + defaultConfig.versionCode) ndk { abiFilter "armeabi" } } fat } }
Здесь мы поставили префикс 6 для x86, 4 для mips, 2 для ARMv7 и 1 для ARMv5.
Работа с ndk в Android Studio
Если в исходниках проекта есть папка jni, система сборки попытается вызвать ndk-build автоматически.
В текущей реализации ваши Android.mk мейкфайлы игнорируются, вместо них создается новый на лету. Это действительно удобно для небольших проектов (вам больше вообще не требуются .mk файлы!), но в больших может раздражать, если вам требуются все возможности, предоставляемые мейкфайлами. Существует возможность отключить это свойство в build.gradle:
android{ ... sourceSets.main.jni.srcDirs = [] //disable automatic ndk-build call }
Если вы хотите использовать генерируемый на лету мейкфайл, вы можете настроить его изначально, установив свойство ndk.moduleName, например, так:
android { ... defaultConfig { ndk { moduleName "hello-jni" } } }
Вы также можете установить другие свойства ndk:
- cFlags,
- ldLibs,
- stl (т.е.: gnustl_shared, stlport_static …),
- abiFilters (т.е.: «x86», «armeabi-v7a»).
Генерация отладочного APK достигается заданием значения true для свойства android.buildTypes.debug.jniDebugBuild; в этом случае ndk-build будет передано NDK_DEBUG=1.
Если вы используете RenderScript из NDK, вам потребуется установить значение true для свойства defaultConfig.renderscriptNdkMode.
Если вы доверяете авто-генерируемым мейкфайлам, то можете задавать различные cFlags в зависимости от конечной архитектуры, когда вы собираете многоархитектурные APK. Так что если вы хотите полностью довериться gradle, мы рекомендуем генерировать различные APK для архитектур, используя ранее описанные модификаторы конфигурации:
... productFlavors { x86 { versionCode Integer.parseInt("6" + defaultConfig.versionCode) ndk { cFlags cFlags + " -mtune=atom -mssse3 -mfpmath=sse" abiFilter "x86" } } ...
Мой пример файла .gradle
Соединяя все вместе, привожу файл build.gradle, который сам сейчас использую. В нем нет модификаторов для различных поддерживаемых ABI, он не использует интеграцию с ndk-build, поэтому работает в Windows окружении и не требует ни изменения обычных мест расположения исходников и библиотек, ни содержимого моих .mk файлов.
import java.util.regex.Pattern buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.9.0' } } apply plugin: 'android' android { compileSdkVersion 19 buildToolsVersion "19.0.3" defaultConfig{ versionCode getVersionCodeFromManifest() } sourceSets.main { jniLibs.srcDir 'src/main/libs' jni.srcDirs = [] //disable automatic ndk-build call } productFlavors { x86 { versionCode Integer.parseInt("6" + defaultConfig.versionCode) ndk { abiFilter "x86" } } mips { versionCode Integer.parseInt("4" + defaultConfig.versionCode) ndk { abiFilter "mips" } } armv7 { versionCode Integer.parseInt("2" + defaultConfig.versionCode) ndk { abiFilter "armeabi-v7a" } } arm { versionCode Integer.parseInt("1" + defaultConfig.versionCode) ndk { abiFilter "armeabi" } } fat } } def getVersionCodeFromManifest() { def manifestFile = file(android.sourceSets.main.manifest.srcFile) def pattern = Pattern.compile("versionCode=\"(\\d+)\"") def matcher = pattern.matcher(manifestFile.getText()) matcher.find() return Integer.parseInt(matcher.group(1)) }
Устранение неисправностей
NDK не сконфигурирован
Если вы получаете ошибку:
Execution failed for task ':app:compileX86ReleaseNdk'. > NDK not configured
Это означает, что инструменты не найдены в каталоге NDK. Имеется два выхода: установить переменную ANDROID_NDK_HOME в соответствии с вашим каталогом NDK и удалить local.properties или же установить ее вручную внутри local.properties:
ndk.dir=C\:\\Android\\ndk
Не существует правила для создания цели
Если вы получаете ошибку:
make.exe: *** No rule to make target ...\src\main\jni
Ее причиной может быть имеющаяся ошибка в NDK для Windows, когда имеется только один исходный файл для компилирования. Добавьте еще один пустой файл – и все заработает.
Прочие вопросы
Вы, возможно, сможете найти ответы на интересующие вас вопросы в google группе adt-dev.
Получение информации по интеграции NDK
Лучшее место для нахождения более подробной информации – официальная страница проекта.
Посмотрите на список изменений и, если пролистаете его целиком, найдете примеры проектов, связанных с интеграцией NDK, внутри самых свежих архивов gradle-samples-XXX.zip.
На видео ниже показано, как настроить проект с исходниками NDK из Android Studio.
ссылка на оригинал статьи http://habrahabr.ru/company/intel/blog/216353/
Добавить комментарий