Gradle android build для разных процессорных архитектур

Я хочу построить 4 отдельных apks для 4 различных архитектур процессоров Android (armeabi armeabi-v7a x86 mips), используя Gradle.

У меня есть собственные библиотеки OpenCV, построенные для 4 архитектур процессора в папке libs .

libs -armeabi -armeabi-v7a -x86 -mips 

Я хочу, чтобы каждый apk содержал только библиотеку OpenCV, соответствующую правильной архитектуре процессора.

Текущий скрипт сборки выглядит следующим образом:

 apply plugin: 'android' dependencies { compile fileTree(dir: 'libs', include: '*.jar') compile project(':workspace:OpenCV4Android:sdk:java') } android { compileSdkVersion 11 buildToolsVersion "18.1.0" sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } // Move the tests to tests/java, tests/res, etc... instrumentTest.setRoot('tests') debug.setRoot('build-types/debug') release.setRoot('build-types/release') flavorGroups "abi", "version" productFlavors { x86 { flavorGroup "abi" } arm { flavorGroup "abi" } mips { flavorGroup "abi" } } } } 

Может кто-нибудь помочь мне решить это, пожалуйста?

Ура,

Solutions Collecting From Web of "Gradle android build для разных процессорных архитектур"

Начиная с версии Android Gradle Plugin версии 13 вы можете создавать отдельные APK, используя новый механизм «split». Вы можете прочитать об этом здесь .

Структура файлов по умолчанию для размещения ваших файлов .so:

 src -main -jniLibs -armeabi -arm.so -armeabi-v7a -armv7.so -x86 -x86.so -mips -mips.so 

Обратите внимание, что имя файла .so не имеет значения, если оно имеет расширение .so.

Затем в файле сборки Gradle:

 android { ... splits { abi { enable true reset() include 'x86', 'armeabi-v7a', 'mips', 'armeabi' universalApk false } } } 

а также

 // map for the version code ext.versionCodes = ['armeabi-v7a':1, mips:2, x86:3] import com.android.build.OutputFile android.applicationVariants.all { variant -> // assign different version code for each output variant.outputs.each { output -> output.versionCodeOverride = project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) * 1000000 + android.defaultConfig.versionCode } } 

Обратите внимание, что коды версий, приведенные выше в ext.versionCodes, в значительной степени неактуальны, это здесь, чтобы добавить уникальное смещение для каждого типа ABI, поэтому коды версий не сталкиваются.

Расколотое решение ABI APK для градиента является самым простым, что я нашел до сих пор. @withoutclass имеет хорошую запись здесь: https://stackoverflow.com/a/26129447/254573 Мне пришлось ссылаться на документацию на Android, так как это новая функция, которую можно изменить: http://tools.android.com/tech -docs / новая сборка-система / пользователь руководство / APK-расколы

Тем не менее, мне пришлось отказаться от этой простой реализации, так как мне нужно было поддерживать как сборку с живыми строками, так и архитектуру. Вы можете столкнуться с этой же проблемой, если вы поддерживаете как хранилище Google Play (которое поддерживает APK с архитектурой), так и App Store Амазонки (который поддерживает только APK с жиром).

Возможно, это будет возможно с помощью split APK, если вы можете добавить компонент вкуса, но пока что split + flavor еще не поддерживается: https://code.google.com/p/android/issues/detail?id= 76469

Я закончил использование abiFilter, см. Пример кода ниже:

 android { flavorDimensions "abi" productFlavors { fat { flavorDimension "abi" ndk { abiFilters "x86", "armeabi-v7a", "armeabi" versionCode = 0; } } arm { flavorDimension "abi" ndk { abiFilter "armeabi" versionCode = 1; } } armv7a { flavorDimension "abi" ndk { abiFilter "armeabi-v7a" versionCode = 3; } } x86 { flavorDimension "abi" ndk { abiFilter "x86" versionCode = 6; } } } } // Each APK needs a different version code when submitted to Google, // bump the versionCode we set in defaultConfig android.applicationVariants.all { variant -> // Ugly hard coded flavorDimensions position // If you have more than one flavorDimension, make sure to target the position for "abi" def abiVersion = variant.productFlavors.get(0).versionCode variant.mergedFlavor.versionCode = abiVersion * 1000 + android.defaultConfig.versionCode } 

Обновление. Используя universalApk, установленное в true, решает это решение, просто добавляет время для сборки каждого apk.

 android { // Rest of Gradle file splits { abi { enable true reset() include 'armeabi', 'armeabi-v7a', 'x86' universalApk true } } } //Ensures architecture specific APKs have a higher version code //(otherwise an x86 build would end up using the arm build, which x86 devices can run) ext.versionCodes = [armeabi:1, 'armeabi-v7a':3, x86:6] android.applicationVariants.all { variant -> // assign different version code for each output variant.outputs.each { output -> int abiVersionCode = project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) ?: 0 output.versionCodeOverride = (abiVersionCode * 1000) + android.defaultConfig.versionCode } } 

ОБНОВЛЕНИЕ – со времени этой публикации в процессе построения градиентов был достигнут значительный прогресс, поэтому этот ответ может быть не рекомендованной лучшей практикой, и новые изменения могут даже остановить его. Используйте свое собственное усмотрение.

Для этого сначала необходимо поместить собственные библиотеки в следующую иерархию папок отдельно

 lib -armeabi -arm.so -*.so 

 lib -x86 -x86.so -*.so 

Затем застегните папки lib (без 's') (например, arm.zip и x86.zip) и переименуйте расширение «zip» в «jar» (например, arm.jar и x86.jar). Поместите эти банки в соответствующие папки (например, armeabi / libs и x86 / libs). Теперь мы собираемся включить зависимости для каждого аромата. Но мы не можем использовать «файл компиляции» … ». Мы должны использовать файл «flavorCompile» … »

например

  flavorGroups 'abi' productFlavors { arm { flavorGroup 'abi' dependencies { armCompile files('arm/libs/armeabi.jar') } } x86 { flavorGroup 'abi' dependencies { x86Compile files('x86/libs/x86.jar') } } } 

====

Вот более сложная среда. У вас не только варианты архитектуры процессора, но также у вас есть библиотеки отладки ( .jar, .so) для процессоров. Пример здесь имеет Debug.jar для отладки руки и NonDebug.jar для выпуска Arm; И * .so для обеих рук и X86. Такая конфигурация может быть достигнута с помощью gradle ExtraPropertiesExtension. Пожалуйста, прочтите мой SO-ответ здесь, https://stackoverflow.com/a/19941684/319058 , чтобы понять, как можно структурировать папки отладки.

 android { compileSdkVersion 18 buildToolsVersion "19.0.0" final DEBUG_ROOT = "build-types/debug" final RELEASE_ROOT = "build-types/release" project.ext.set("projRoot", "") buildTypes { debug { project.projRoot = DEBUG_ROOT dependencies { debugCompile files(DEBUG_ROOT+"/libs/Debug.jar") } } release { project.projRoot = RELEASE_ROOT dependencies { releaseCompile files(RELEASE_ROOT+"/libs/NonDebug.jar") } runProguard true proguardFile 'proguard.cfg' } } sourceSets { final PROJ_ROOT = project.ext.get("projRoot") final BUILD_TYPE_RES = PROJ_ROOT + "/res" main { manifest.srcFile 'src/main/AndroidManifest.xml' java.srcDirs = ['src/main/java'] //resources.srcDirs = ['src/main'] //aidl.srcDirs = ['src/main'] //renderscript.srcDirs = ['src/main'] res.srcDirs = ['src/main/res',BUILD_TYPE_RES] assets.srcDirs = ['src/main/assets'] } flavorGroups 'abi' productFlavors { arm { flavorGroup 'abi' final ARM_LIB_PATH = PROJ_ROOT + "/arm/libs/armeabi.jar" dependencies { armCompile files(ARM_LIB_PATH) } } x86 { flavorGroup 'abi' final X86_LIB_PATH = PROJ_ROOT + "/x86/libs/x86.jar" dependencies { x86Compile files(X86_LIB_PATH) } } } // Move the tests to tests/java, tests/res, etc... instrumentTest.setRoot('tests') // Move the build types to build-types/<type> // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ... // This moves them out of them default location under src/<type>/... which would // conflict with src/ being used by the main source set. // Adding new build types or product flavors should be accompanied // by a similar customization. debug.setRoot(DEBUG_ROOT) release.setRoot(RELEASE_ROOT) } 

}

У меня нет ответа на градле, но я думаю, что теперь у меня есть общий ответ для любого инструмента для создания Android. Вот моя идея о том, как создавать отдельные файлы APK для каждой поддерживаемой архитектуры процессора:

  1. Создайте APK с любыми инструментами, которые вы используете, включая все поддерживаемые базовые библиотеки кода, например armeabi, armeabi-v7a, x86 и mips. Я назову его «оригинальным» APK-файлом.

  2. Разархивируйте ваш оригинальный APK в пустую папку, используя любую утилиту zip / unzip, лучше всего используйте инструменты командной строки, чтобы впоследствии можно было автоматизировать ее с помощью сценария оболочки или командного файла.

  3. В папке, где оригинальный APK был несжатым, удалите подпапку META-INF (это содержит подписи, нам нужно будет снова подписать APK после всех модификаций, чтобы исходный META-INF был удален).

  4. Перейдите в подпапку lib и удалите подпапки для любых архитектур процессоров, которые вы не хотите в новом файле APK. Например, оставьте только подкаталог «x86», чтобы сделать APK для процессоров Intel Atom.

  5. Важно: каждый APK для другой архитектуры должен иметь другой номер «versionCode» в AndroidManifest.xml, а код версии, например, armeabi-v7a, должен быть немного выше, чем у armeabi (читайте инструкции Google для создания нескольких APK здесь: http://developer.android.com/google/play/publishing/multiple-apks.html ). К сожалению, файл манифеста находится в скомпилированной двоичной форме внутри APK. Нам нужен специальный инструмент для модификации версии Code. Смотри ниже.

  6. После того, как манифест изменен с новым кодом версии, и ненужные каталоги и файлы удалены, повторно запишите, подпишите и выровняйте свой меньший APK (используйте инструменты jarsigner и zipalign из Android SDK).

  7. Повторите процесс для всех других архитектур, которые вам нужны для поддержки, создавая небольшие файлы APK со слегка отличающимися кодами версий (но с тем же именем версии).

Единственной нерешенной проблемой является способ изменения «versionCode» в двоичном файле манифеста. Я не мог найти решение для этого в течение долгого времени, поэтому, наконец, пришлось сесть и запустить мой собственный код, чтобы сделать это. В качестве отправной точки я взял APKExtractor от Prasanta Paul, http://code.google.com/p/apk-extractor/ , написанного на Java. Я старая школа и все еще более комфортно с C ++, поэтому моя небольшая утилита «aminc», написанная на C ++, теперь находится на GitHub:

https://github.com/gregko/aminc

Я опубликовал все решение Visual Studio 2012, но вся программа представляет собой один .cpp-файл, который, вероятно, может быть скомпилирован на любой платформе. И вот пример файла Windows .bat, который я использую для разделения моего «жирного» apk с именем atVoice.apk на 4 небольших файла с именем atVoice_armeabi.apk, atVoice_armeabi-v7a.apk, atVoice_x86.apk и atVoice_mips.apk. Я фактически отправляю эти файлы в Google Play (см. Мое приложение на https://play.google.com/store/apps/details?id=com.hyperionics.avar ), и все работает отлично. Также см. Этот проект Github от Jorge Suárez de Lis , который размещает аналогичный сценарий для Linux.

 @echo off REM My "fat" apk is named atVoice.apk. Change below to whatever or set from %1 set apkfile=atVoice del *.apk REM My tools build atVoice-release.apk in bin project sub-dir. REM Copy it here for splitting. copy ..\bin\%apkfile%-release.apk %apkfile%.apk zip -d %apkfile%.apk META-INF/* REM ------------------- armeabi ------------------------ unzip %apkfile%.apk AndroidManifest.xml copy/y %apkfile%.apk %apkfile%.zip zip -d %apkfile%.zip lib/armeabi-v7a/* lib/x86/* lib/mips/* aminc AndroidManifest.xml 1 zip -f %apkfile%.zip ren %apkfile%.zip %apkfile%_armeabi.apk jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_armeabi.apk MyKeyName zipalign 4 %apkfile%_armeabi.apk %apkfile%_armeabi-aligned.apk del %apkfile%_armeabi.apk ren %apkfile%_armeabi-aligned.apk %apkfile%_armeabi.apk REM ------------------- armeabi-v7a --------------------- copy/y %apkfile%.apk %apkfile%.zip zip -d %apkfile%.zip lib/armeabi/* lib/x86/* lib/mips/* aminc AndroidManifest.xml 1 zip -f %apkfile%.zip ren %apkfile%.zip %apkfile%_armeabi-v7a.apk jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_armeabi-v7a.apk MyKeyName zipalign 4 %apkfile%_armeabi-v7a.apk %apkfile%_armeabi-v7a-aligned.apk del %apkfile%_armeabi-v7a.apk ren %apkfile%_armeabi-v7a-aligned.apk %apkfile%_armeabi-v7a.apk REM ------------------- x86 --------------------- copy/y %apkfile%.apk %apkfile%.zip zip -d %apkfile%.zip lib/armeabi/* lib/armeabi-v7a/* lib/mips/* aminc AndroidManifest.xml 9 zip -f %apkfile%.zip ren %apkfile%.zip %apkfile%_x86.apk jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_x86.apk MyKeyName zipalign 4 %apkfile%_x86.apk %apkfile%_x86-aligned.apk del %apkfile%_x86.apk ren %apkfile%_x86-aligned.apk %apkfile%_x86.apk REM ------------------- MIPS --------------------- copy/y %apkfile%.apk %apkfile%.zip zip -d %apkfile%.zip lib/armeabi/* lib/armeabi-v7a/* lib/x86/* aminc AndroidManifest.xml 10 zip -f %apkfile%.zip ren %apkfile%.zip %apkfile%_mips.apk jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_mips.apk MyKeyName zipalign 4 %apkfile%_mips.apk %apkfile%_mips-aligned.apk del %apkfile%_mips.apk ren %apkfile%_mips-aligned.apk %apkfile%_mips.apk del AndroidManifest.xml del %apkfile%.apk :done 

Greg