Android: construye APKs separados para diferentes arquitecturas de procesador

¿Existe una manera fácil de construir archivos APK separados para Android para diferentes arquitecturas de procesador, con la antigua ANT o el nuevo proceso de generación de Gradle? Mi manera de hacer esto es construir un APK "graso" con todas las bibliotecas nativas soportadas incluidas, y luego dividirlas en APK separado como he explicado aquí . Sin embargo, parece que debería haber un método más directo de hacer esto …

Decidí volver a publicar mi respuesta de otra parte aquí, de modo que todo esto esté en una página para el acceso fácil. Si esto es contra las políticas de SO, por favor, díganme y elimine este mensaje desde aquí.

Aquí está mi idea sobre cómo crear archivos APK separados para cada arquitectura de procesador soportada:

  1. Construye un APK "grueso" con las herramientas que uses, conteniendo todas las bibliotecas de código nativo que soporte, por ejemplo, armeabi, armeabi-v7a, x86 y mips. Lo llamaré el archivo APK "original".

  2. Descomprima su APK original en una carpeta vacía, con cualquier utilidad zip / unzip, utilice mejor las herramientas de línea de comandos, para poder automatizarla con una secuencia de comandos de shell o un archivo por lotes más adelante. En realidad, como muestra mi secuencia de comandos de lotes de muestra que aparece a continuación, sólo uso las herramientas zip / unzip de línea de comandos para manipular APK directamente, en lugar de descomprimirlas completamente, pero el efecto es el mismo.

  3. En la carpeta donde se descomprimió el APK original (o en el archivo .apk / .zip original), borre la subcarpeta META-INF (que contiene las firmas, debemos volver a firmar el APK después de todas las modificaciones, por lo que El META-INF original debe ser borrado).

  4. 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 sólo la subcarpeta 'x86' para crear un APK para los procesadores Intel Atom.

  5. Importante: cada APK para una arquitectura diferente debe tener un número 'versionCode' diferente en AndroidManifest.xml y el código de versión para, por ejemplo, armeabi-v7a debe ser ligeramente superior al de armeabi (lea las instrucciones de Google para crear varios APK aquí: http://developer.android.com/google/play/publishing/multiple-apks.html ). Desafortunadamente, el archivo de manifiesto está en una forma binaria compilada dentro del APK. Necesitamos una herramienta especial para modificar el código de versión allí. Vea abajo.

  6. Una vez modificado el manifiesto con un nuevo código de versión, y los directorios y archivos innecesarios eliminados, vuelva a cerrar, firme y alinee su APK más pequeño (utilice las herramientas jarsigner y zipalign de Android SDK).

  7. Repita el proceso para todas las demás arquitecturas que necesite soportar, creando archivos APK más pequeños con códigos de versión ligeramente diferentes (pero con el mismo nombre de versión).

La única cuestión pendiente es la forma de modificar 'versionCode' en el archivo de manifiesto binario. No pude encontrar una solución para esto durante mucho tiempo, por lo que finalmente tuve que sentarse y poner en marcha mi propio código para hacer esto. Como punto de partida, tomé APKExtractor por Prasanta Paul, http://code.google.com/p/apk-extractor/ , escrito en Java. Soy la vieja escuela y aún 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

He publicado toda la solución de Visual Studio 2012 allí, pero todo el programa es un archivo .cpp único que probablemente se puede compilar en cualquier plataforma. Y aquí hay un ejemplo de archivo de script de lotes de Windows que utilizo para dividir mi apk "gordo" llamado atVoice.apk en 4 archivos más pequeños llamados atVoice_armeabi.apk, atVoice_armeabi-v7a.apk, atVoice_x86.apk y atVoice_mips.apk. Realmente envío estos archivos a Google Play (ver mi aplicación en https://play.google.com/store/apps/details?id=com.hyperionics.avar ) y todo funciona perfectamente:

@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 herefor 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 

Salvaguardias adicionales

Recibo algunos informes de error en la consola de desarrolladores de Google Play, indicando que no se pudo encontrar un método nativo. Lo más probable es que esto se debe a que el usuario instala un APK incorrecto, por ejemplo, Intel o MIPS APK en un dispositivo ARM. Agregado código extra a mi aplicación, comprobar el número de VersionCode contra Build.CPU_ABI, luego mostrar un mensaje de error en caso de desajuste, pedir al usuario que vuelva a instalar desde Google Play (o mi propio sitio web, donde publico un APK "gordo" ) En tal caso.

Greg

En este artículo Android NDK: esquema de código de versión para la publicación de APKs por arquitectura He encontrado una buena solución a este problema. Consiste en agregar el siguiente código

  splits { abi { enable true reset() include 'x86', 'armeabi', 'armeabi-v7a' universalApk true } } project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9] android.applicationVariants.all { variant -> variant.outputs.each { output -> output.versionCodeOverride = project.ext.versionCodes.get(output.getFilter( com.android.build.OutputFile.ABI), 0) * 10000000 + android.defaultConfig.versionCode } } 

A la sección android{...} del script build.gradle. Si quieres entender los detalles, te recomiendo que leas ese artículo, realmente vale la pena leerlo.

Comience con Android NDK construir con ANT script , con un cambio mínimo:

 <target name="-pre-build"> <exec executable="${ndk.dir}/ndk-build" failonerror="true"/> <arg value="APP_ABI=${abi}"/> </target> 

Y utilizar un archivo por lotes para ejecutar el bucle (uso un script sed simple, sed está disponible en %NDK_ROOT%\prebuilt\windows\bin\ y en todas las demás plataformas):

 sed -i -e "s/versionCode=\"\([0-9]*\).]\"/versionCode=\"\11\"/" AndroidManifest.xml ant -Dsdk.dir=%SDK_ROOT% -Dndk.dir=%NDK_ROOT% -Dabi=armeabi release ren %apkfile%.apk %apkfile%_armeabi.apk sed -i -e "s/versionCode=\"\([0-9]*\).\"/versionCode=\"\12\"/" AndroidManifest.xml ant -Dsdk.dir=%SDK_ROOT% -Dndk.dir=%NDK_ROOT% -Dabi=mips release ren %apkfile%.apk %apkfile%_mips.apk sed -i -e "s/versionCode=\"\([0-9]*\).\"/versionCode=\"\13\"/" AndroidManifest.xml ant -Dsdk.dir=%SDK_ROOT% -Dndk.dir=%NDK_ROOT% -Dabi=armeabi-v7a release ren %apkfile%.apk %apkfile%_armeabi-v7a.apk sed -i -e "s/versionCode=\"\([0-9]*\).\"/versionCode=\"\14\"/" AndroidManifest.xml ant -Dsdk.dir=%SDK_ROOT% -Dndk.dir=%NDK_ROOT% -Dabi=x86 release ren %apkfile%.apk %apkfile%_x86.apk 

Esto supone que android.verisonCode en el archivo de manifiesto tiene cero como último dígito, por ejemplo android:versionCode="40260" .

Tenga en cuenta que técnicamente no hay razón para cambiar versionCode para armeabi y mips variantes, pero puede ser importante mantener armeabi <armeabi-v7a <x86 .

Actualización Gracias a Ashwin S Ashok por proponer un esquema de numeración aún mejor: VERSION+10000*CPU .

Una forma muy fácil de hacerlo es mediante el uso de una plataforma multiplataforma como Intel XDK.

Tiene la capacidad de crear aplicaciones que funcionarán en todas las versiones de Android (2.3 +), iOS, Windows Phone 8, Tizen, Google Chrome y Mozilla Firefox.

Un código fuente hará la magia para usted.

  • Android NDK no puede depurar código nativo con dos complementos de gradle
  • Android JNI nativo C función llamada mata actividad
  • Cómo utilizar OpenSSL Library en la aplicación ANDROID
  • Acerca de llamar método nativo en android
  • Registro de llamadas de Android La voz entrante no se graba
  • Native Android Crash - Mapa de bits no válido
  • Android: incluye funciones nativas de StageFright en mi propio proyecto
  • SIG33 al depurar el Android nativo
  • ¿Es posible crear elementos de interfaz de usuario con el NDK? - falta de especificaciones en documentos Android
  • OpenGL en Android: ¿Algún conflicto al llamar a funciones de OpenGL en Java y C ++?
  • Reaccionar Native Android y Genymotion
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.