Android: construya APK por separado para diferentes architectures de procesador

¿Existe una manera fácil de crear archivos APK por separado para Android para diferentes architectures de procesador, con la antigua ANT o el nuevo proceso de comstackción de Gradle? Mi forma de hacerlo es crear un APK “grueso” con todas las bibliotecas nativas admitidas incluidas, y luego dividirlas en APK por separado, como expliqué aquí . Sin embargo, parece que debería haber un método más directo para hacer esto …

    Decidí volver a publicar mi respuesta desde otro lugar aquí, para que todo esto esté en una página para facilitar el acceso. Si esto está en contra de las políticas de SO, por favor dígame y elimine esta publicación desde aquí.

    Aquí está mi idea sobre cómo crear archivos APK por separado para cada architecture de procesador compatible:

    1. Cree un APK “grueso” con las herramientas que use, que contenga todas las bibliotecas de códigos nativos que admita, 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 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 realidad, como muestra mi script de lote de muestra publicado a continuación, simplemente uso herramientas de compresión / descomprimir línea de comando para manipular APK directamente, en lugar de descomprimirlos por completo, pero el efecto es el mismo.

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

    4. Cambie a la subcarpeta lib y elimine las subcarpetas de las architectures 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.

    5. Importante: cada APK para una architecture 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 comstackdo dentro del APK. Necesitamos una herramienta especial para modificar el código de versión allí. Vea abajo.

    6. 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).

    7. Repite el proceso para todas las demás architectures 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 progtwig de utilidad ‘aminc’ escrito en C ++ ahora está en GitHub en:

    https://github.com/gregko/aminc

    Publiqué allí toda la solución de Visual Studio 2012, pero todo el progtwig es un único archivo .cpp que probablemente se puede comstackr en cualquier plataforma. Y aquí hay un ejemplo del archivo de script por lotes 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 (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 errores en la consola del desarrollador de Google Play, que indican que no se pudo encontrar un método nativo. Lo más probable es que esto se deba a que el usuario está instalando un APK incorrecto, por ejemplo, Intel o MIPS APK en un dispositivo ARM. Agregué código extra a mi aplicación, comprobando el número de VersionCode contra Build.CPU_ABI, luego mostrando un mensaje de error en caso de discrepancia, pidiendo al usuario que reinstale desde Google Play (o mi propio sitio web, donde publico una aplicación “gorda”) ) En tal caso.

    Greg

    En este artículo Android NDK: esquema de código de versión para publicar APK por architecture , 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 la comstackción de Android NDK con script ANT , con un cambio mínimo:

         

    y utilice un archivo de proceso por lotes para ejecutar el ciclo (utilizo 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 ninguna razón para cambiar versionCode para variantes de armeabi y mips , pero puede ser importante mantener armeabi .

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

    Una forma muy sencilla de hacerlo es mediante el uso de un marco 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 por ti.