11

是否有一种简单的方法可以使用旧的 ANT 或新的 Gradle 构建过程为不同的处理器架构为 Android 构建单独的 APK 文件?我这样做的方法是构建一个包含所有受支持的本机库的“胖”APK,然后按照我在此处的解释将它们拆分为单独的 APK 。但是,似乎应该有更直接的方法来做到这一点......

4

3 回答 3

6

我决定从这里的其他地方重新发布我的答案,以便所有这些都在一个页面上以便于访问。如果这违反了 SO 政策,请告诉我并从此处删除此帖子。

这是我关于如何为每个支持的处理器架构创建单独的 APK 文件的想法:

  1. 使用您使用的任何工具构建一个“胖”APK,包含您支持的所有本机代码库,例如 armeabi、armeabi-v7a、x86 和 mips。我将其称为“原始”APK 文件。

  2. 使用任何 zip/unzip 实用程序将您的原始 APK 解压缩到一个空文件夹中,最好使用命令行工具,以便您以后可以使用 shell 脚本或批处理文件将其自动化。实际上,正如下面发布的示例批处理脚本所示,我只是使用命令行 zip/unzip 工具直接操作 APK,而不是完全解压缩它们,但效果是一样的。

  3. 在原始 APK 解压到的文件夹中(或原始 .apk/.zip 中),删除 META-INF 子文件夹(其中包含签名,我们需要在所有修改后重新签名 APK,所以必须删除原始 META-INF)。

  4. 更改为 lib 子文件夹,并删除新 APK 文件中不需要的任何处理器架构的子文件夹。例如,只保留“x86”子文件夹来为英特尔凌动处理器制作 APK。

  5. 重要提示:针对不同架构的每个 APK,在 AndroidManifest.xml 中必须有不同的“versionCode”编号,例如 armeabi-v7a 的版本代码必须略高于 armeabi 的版本代码(请阅读此处创建多个 APK 的 Google 说明:http://developer.android.com/google/play/publishing/multiple-apks.html)。不幸的是,清单文件在 APK 中是编译后的二进制形式。我们需要一个特殊的工具来修改那里的 versionCode。见下文。

  6. 使用新版本代码修改清单并删除不必要的目录和文件后,重新压缩、签名并对齐较小的 APK(使用 Android SDK 中的 jarsigner 和 zipalign 工具)。

  7. 对您需要支持的所有其他架构重复该过程,创建版本代码略有不同(但版本名称相同)的较小 APK 文件。

唯一突出的问题是在二进制清单文件中修改“versionCode”的方法。很长一段时间我都找不到解决方案,所以最后不得不坐下来编写自己的代码来做到这一点。作为起点,我使用了 Prasanta Paul 的 APKExtractor,http://code.google.com/p/apk-extractor/,它是用 Java 编写的。我是老派,对 C++ 更熟悉,所以我用 C++ 编写的小实用程序“aminc”现在在 GitHub 上:

https://github.com/gregko/aminc

我在那里发布了整个 Visual Studio 2012 解决方案,但整个程序是一个 .cpp 文件,可能可以在任何平台上编译。这是一个示例 Windows 批处理脚本文件,我使用它将名为 atVoice.apk 的“胖”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上查看我的应用程序)并且一切正常:

@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

额外的保障

我在 Google Play 开发者控制台收到了一些错误报告,指出找不到本机方法。这很可能是因为用户在 ARM 设备上安装了错误的 APK,例如 Intel 或 MIPS APK。向我的应用程序添加了额外的代码,对照 Build.CPU_ABI 检查 VersionCode 编号,然后在不匹配的情况下显示错误消息,要求用户从 Google Play(或我自己的网站,我在其中发布“胖”APK)重新安装) 在这种情况下。

格雷格

于 2013-11-14T13:14:53.920 回答
6

在这篇文章Android NDK: Version code scheme for publishing APKs per architecture我找到了一个很好的解决这个问题的方法。它包括添加以下代码

 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
    }
}

android{...}build.gradle 脚本的部分。如果你想了解细节,我强烈推荐阅读那篇文章,真的很值得一读。

于 2016-10-12T12:40:52.147 回答
-1

从使用ANT 脚本的 Android NDK 构建开始,只需进行少量更改:

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

并使用批处理文件来运行循环(我使用一个简单的sed脚本;sed%NDK_ROOT%\prebuilt\windows\bin\在所有其他平台上都可用):

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

这假设清单文件中的android.verisonCode的最后一位数字为零,例如android:versionCode="40260".

请注意,从技术上讲,没有理由更改armeabimips变体的 versionCode,但保持armeabi < armeabi-v7a < x86可能很重要。

更新感谢Ashwin S Ashok提出了一个更好的编号方案:VERSION+10000*CPU.

于 2013-11-17T09:41:12.233 回答