studio - Gradle android build para diferentes arquitecturas de procesador
flavor android (4)
Quiero construir 4 aplicaciones por separado para 4 arquitecturas diferentes de procesadores de CPU Android (armeabi armeabi-v7a x86 mips) usando Gradle.
Tengo librerías nativas OpenCV creadas para 4 arquitecturas de CPU en la carpeta libs .
libs
-armeabi
-armeabi-v7a
-x86
-mips
Quiero que cada apk solo contenga la biblioteca OpenCV correspondiente a la arquitectura de CPU correcta.
El script de compilación actual es el siguiente:
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"
}
}
}
}
¿Alguien puede ayudarme a resolver esto por favor?
Aclamaciones,
A partir de Android Gradle Plugin versión 13 ahora puede generar archivos APK separados utilizando el nuevo mecanismo de "división". Puedes leer sobre esto here .
La estructura de archivos predeterminada para colocar sus archivos .so es:
src
-main
-jniLibs
-armeabi
-arm.so
-armeabi-v7a
-armv7.so
-x86
-x86.so
-mips
-mips.so
Tenga en cuenta que el nombre del archivo .so no es importante siempre que tenga la extensión .so.
Luego en tu archivo de compilación Gradle:
android {
...
splits {
abi {
enable true
reset()
include ''x86'', ''armeabi-v7a'', ''mips'', ''armeabi''
universalApk false
}
}
}
y
// 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
}
}
Tenga en cuenta que los códigos de versión anteriores en ext.versionCodes son en gran parte irrelevantes, aquí está para agregar un desplazamiento único para cada tipo de ABI, por lo que los códigos de versión no entran en conflicto.
ACTUALIZACIÓN: desde el momento de esta publicación, se ha avanzado mucho en el proceso de compilación de gradle, por lo tanto, esta respuesta podría no ser la mejor práctica recomendada y los nuevos cambios podrían incluso frenarla. Use su propio criterio
Para hacer eso, primero, debe colocar las bibliotecas nativas en la siguiente jerarquía de carpetas por separado
lib
-armeabi
-arm.so
-*.so
-
lib
-x86
-x86.so
-*.so
luego comprime las carpetas lib (sin ''s'') (por ejemplo, arm.zip y x86.zip) y cambia el nombre de la extensión ''zip'' a ''jar'' (por ejemplo, arm.jar y x86.jar). Coloque estos frascos en las carpetas apropiadas (p. Ej., Armeabi / libs y x86 / libs). Ahora vamos a incluir las dependencias para cada sabor. Pero no podemos usar "compilar archivo ''...''". Tenemos que usar "flavorCompile file ''...''"
p.ej
flavorGroups ''abi''
productFlavors {
arm {
flavorGroup ''abi''
dependencies {
armCompile files(''arm/libs/armeabi.jar'')
}
}
x86 {
flavorGroup ''abi''
dependencies {
x86Compile files(''x86/libs/x86.jar'')
}
}
}
====
Aquí hay un entorno más complejo. No solo tiene variantes de arquitectura de procesador sino que también tiene bibliotecas de depuración ( .jar, .so) para los procesadores. El ejemplo aquí tiene como Debug.jar para Arm debug y NonDebug.jar para Arm; y * .so para ambos, Arm y X86. Dicha configuración se puede lograr utilizando gradle ExtraPropertiesExtension Lea mi respuesta SO aquí, https://.com/a/19941684/319058 , para comprender cómo se pueden estructurar las carpetas de depuración.
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)
}
}
La solución ABI APK dividida para gradle es la más simple que he encontrado hasta ahora. @withoutlass tiene una buena reseña aquí: https://.com/a/26129447/254573 Tuve que hacer referencia a la documentación de Android ya que esta es una característica nueva que aún puede cambiar: http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
Sin embargo, terminé teniendo que abandonar esta implementación simple ya que necesitaba admitir compilaciones de gran tamaño y construcciones específicas de arquitectura. Es posible que se encuentre con este mismo problema si admite tanto la tienda de Google Play (que admite archivos APK específicos de la arquitectura) como la tienda de aplicaciones de Amazon (que solo admite APK de gran tamaño).
Es posible hacer esto con archivos APK divididos si puede agregar un componente de sabor, pero a partir de ahora no se admite la división + sabor: https://code.google.com/p/android/issues/detail?id=76469
Terminé usando abiFilter, mira el código de muestra a continuación:
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
}
Actualizar Usar universalApk establecido en true soluciona esta solución, simplemente agrega tiempo para compilar cada 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
}
}
No tengo una respuesta gradle, pero creo que ahora tengo una respuesta genérica para cualquier herramienta de compilación de Android. Aquí está mi idea sobre cómo crear archivos APK por separado para cada arquitectura de procesador compatible:
Cree su APK con las herramientas que use, que contengan todas las bibliotecas de códigos nativos que admita, por ejemplo, armeabi, armeabi-v7a, x86 y mips. Lo llamaré el archivo APK ''original''.
Descomprima su APK original en una carpeta vacía, con cualquier utilidad de descomprimir / descomprimir, utilice mejor las herramientas de línea de comandos, de modo que pueda automatizarlo con un script de shell o un archivo por lotes más adelante.
En la carpeta donde se descomprimió el APK original, elimine la subcarpeta META-INF (esta contiene las firmas, necesitaremos volver a firmar el APK después de todas las modificaciones, por lo que se debe eliminar el META-INF original).
Cambie a la subcarpeta lib y elimine las subcarpetas de las arquitecturas de procesador que no desee en el nuevo archivo APK. Por ejemplo, deje solo la subcarpeta ''x86'' para crear una APK para los procesadores Intel Atom.
Importante: cada APK para una arquitectura diferente debe tener un número diferente de ''versionCode'' en AndroidManifest.xml, y el código de versión para, por ejemplo, armeabi-v7a debe ser un poco más alto que el de armeabi (lea las instrucciones de Google para crear múltiples APK aquí: http://developer.android.com/google/play/publishing/multiple-apks.html ). Desafortunadamente, el archivo de manifiesto está en un formulario binario compilado dentro del APK. Necesitamos una herramienta especial para modificar el código de versión allí. Vea abajo.
Una vez que el manifiesto se modifique con un nuevo código de versión y se eliminen los directorios y archivos innecesarios, vuelva a comprimir, firme y alinee su APK más pequeña (use las herramientas jarsigner y zipalign de Android SDK).
Repite el proceso para todas las demás arquitecturas que necesites, creando archivos APK más pequeños con códigos de versión ligeramente diferentes (pero con el mismo nombre de versión).
El único problema pendiente es la forma de modificar ''versionCode'' en el archivo de manifiesto binario. No pude encontrar una solución para esto durante mucho tiempo, así que finalmente tuve que sentarme y poner mi propio código para hacer esto. Como punto de partida, tomé APKExtractor de Prasanta Paul, http://code.google.com/p/apk-extractor/ , escrito en Java. Soy de la vieja escuela y aún me siento más cómodo con C ++, así que mi pequeño programa de utilidad ''aminc'' escrito en C ++ ahora está en GitHub en:
https://github.com/gregko/aminc
Publiqué la solución completa de Visual Studio 2012, pero todo el programa es un único archivo .cpp que probablemente se puede compilar en cualquier plataforma. Y aquí hay un ejemplo del archivo .bat de Windows que uso para dividir mi apk "gorda" llamada atVoice.apk en 4 archivos más pequeños llamados atVoice_armeabi.apk, atVoice_armeabi-v7a.apk, atVoice_x86.apk y atVoice_mips.apk. De hecho, envío estos archivos a Google Play (mira mi aplicación en https://play.google.com/store/apps/details?id=com.hyperionics.avar ) y todo funciona perfectamente. También vea este proyecto Github de Jorge Suárez de Lis , quien publica un script similar para 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