Android - 为不同的处理器架构构建单独的APK

时间:2013-11-13 12:44:53

标签: android build android-ndk apk native

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

3 个答案:

答案 0 :(得分:5)

我决定在此处重新发布我的answer from elsewhere,以便所有这些都在一个页面上以便于访问。如果这违反SO政策,请告诉我并从此处删除此帖。

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

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

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

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

  4. 更改为lib子文件夹,并删除新APK文件中不需要的任何处理器体系结构的子文件夹。例如,只留下'x86'子文件夹为Intel Atom处理器制作APK。

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

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

    < / LI>
  7. 对您需要支持的所有其他体系结构重复此过程,创建版本较小的APK文件(但版本名称略有不同)。

  8. 唯一突出的问题是在二进制清单文件中修改'versionCode'的方法。很长一段时间我找不到解决方案,所以最后不得不坐下来动摇我自己的代码来做到这一点。作为起点,我采用了用Java编写的Prasanta Paul http://code.google.com/p/apk-extractor/的APKExtractor。我是旧学校,对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开发者控制台上收到了一些错误报告,指出无法找到本机方法。最有可能的原因是用户安装了错误的APK,例如ARM设备上的Intel或MIPS APK。为我的应用添加了额外的代码,针对Build.CPU_ABI检查VersionCode编号,然后在出现不匹配的情况下显示错误消息,要求用户从Google Play(或我自己的网站,我发布“胖”APK)重新安装在这种情况下。

    格雷格

答案 1 :(得分:4)

在本文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
    }
}

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

答案 2 :(得分:-1)

Android NDK build with ANT script开始,变化很小:

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

请注意,技术上没有理由更改 armeabi mips 变体的versionCode,但保持 armeabi&lt; armeabi-v7a&lt; 86

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