APK 转 AAB的实现【Android】

1,157 阅读5分钟

按照 Google Play 的政策,现在上架新的应用/游戏,都需要是 AAB 文件,但是有时候并没有直接用 GradleAAB 包的条件。如果能够将 APK 转为 AAB,并且能够正常运行还能上传到 Google Play 那就好办了。所以本文就探讨一下如何实现这个转换。

一、结构对比

首先我们先了解下 APK 的包体结构(左)和 AAB 包体结构(右)的区别。

结构表:

APK 结构AAB 结构
resbase(基础的资源目录)
- assets (对应 APK 中的 assets,但是只存放基础的配置文件,大文件不放此处)
- dex (对应 APKclasses.dex
- lib (对应 APK 中的 lib
- manifest (对应 APK 中的 manifest
- res (对应 APK 中的 res
- root (kotlin、original/META-INF等都在这个文件夹里,具体可以参考文末的对照表)
META-INFMETA-INF (存放元数据)
libmodule (自定义名称的目录,可以存在多个,通常用来分割比较大的资源,避免 150MB 限制)
assetsBUNDLE-METADATA
resources.arscBundleConfig.pb
classes.dex
AndroidManifest.xml

APK 的详细结构这里就不细讲了,我们主要看下 AAB 详细结构,如下图:

那么了解了两者间的区别和对应的目录,接下来我们开始实现转换的流程。

二、转换的实现

整个流程分为四步:反编 APK -> 构建不同的 module -> 合并构建 AAB -> 签名。

1. 工具和环境

首先配置好相应环境和谷歌官方对应的开发工具,如下:

  • java 环境 + jarsigner.exe
  • bundletool.jar
  • android.jar
  • APKtool.jar
  • smali.jar
  • aapt2

JAVA 环境选 8/11,工具的版本最好是用最新的。

2. 反编译 APK

然后使用 APK_tool.jar 进行反编译,得到反编译后的文件夹。

命令行如下:

java -jar [APKtool 文件] [-s (可选)] d [--only -main-classes (可选)] [需要反编的 APK 文件] -o [反编后输出的目录]

这里得到的反编译后的目录如下图:

3. 构建 module

本流程主要是通过编译资源文件,链接资源文件,解压并复制相关文件到目录作为 module

构建 AAB 需要 base 和其他自定义的 module ,在这里用 GameAssetsRes 来当做自定义 module。当然,也可以加其他 module,生成步骤都是一样的。

在构建 module 之前,我们要先学习下 aapt2 的功能,具体可看 官方文档,这里只用到 编译资源文件链接资源文件

3.1 编译资源文件

通过 aapt2 对包含资源文件的目录进行编译,生成 .zip 文件备用。

命令行如下:

[aapt2 执行程序] compile --dir [资源文件夹] -o [生成资源的 zip]

那我们生成的 .zip 文件 是干嘛的呢?解压可以看到里面是各种资源文件编译成的 .flat 文件(由 aapt2 编译而成的二进制文件)。

3.2 链接资源文件

通过 aapt2 link 链接 .zip 以及 AndroidManifest.xml 文件,并将其打包为 .APK

命令行如下:

[aapt2 执行程序] link --proto-format [资源 zip] -o [输出的 .APK] -I [android.jar] --manifest [manifest 文件] --min-sdk-version [最小版本] --target-sdk-version [目标版本] --version-code [版本号] --version-name [版本名] --compile-sdk-version-name [编译 sdk 的版本名]

如果没有 .zip,也可以增加 -R 命令直接指定 .flat 文件,此时可以去掉 -o [输出的 .APK],在命令行末尾加上 --auto-add-overlay (新资源可以覆盖旧资源,不会导致有同样名称的资源冲突)。

PS:[manifest 文件] 参数后面的几个版本相关的参数,为可选配置,配置之后会注入到 AndroidManifest.xml 中。

3.3 构建 base

了解了 aapt2 的两个功能用法之后,我们直接开始构建 base,因为它是最重要的,包含了 res 资源dex 以及 manifest

首先新建一个 base 文件夹,作为构建 base 的主要工作空间,然后开始构建。

构建分为以下几步:

  1. 编译 反编译 APK 步骤后生成目录下的 res 文件夹,生成 compile.zip
  2. 链接 compile.zip 以及反编译目录下的 AndroidManifest.xml,生成 base.apk
  3. 解压 base.apk,将 resAndroidManifest.xmlresources.pb 按照文章末尾的 对照表 ,复制到 base 文件夹下对应位置。
  4. 反编译目录中的其他文件按照附录对照表复制到 base 文件夹下对应位置。(如果游戏的 assets 比较大,则复制必要的配置文件,确保 base.apk 不超过 200MB 即可)
  5. base 文件夹压缩为 base.zip

base.zip 就作为一个 module

4. 构建其他的 module

因为是游戏,所以其他的资源都整合到一个 module 中,我们新建一个 GameAssetsRes(可自定义名称) 文件夹。

步骤和 构建 base 类似,但是因为 res 资源我们都放 base 中了,所以不用再编译资源文件,直接链接资源文件即可。

  1. 没有 .flat 的 .zip,只需要链接下面的 AndroidManifest.xml 即可,生成 GameAssetsRes.APK。(这里选择安装时分发模式 install-time,详细可以了解官方文档
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/APK/res/android"
    xmlns:dist="http://schemas.android.com/APK/distribution"
    package="${applicationId}"
    platformBuildVersionCode="30"
    platformBuildVersionName="11"
    split="GameAssetsRes(可自定义名称)"
    android:compileSdkVersion="30"
    android:compileSdkVersionCodename="11" >
    <dist:module dist:type="asset-pack" >
        <dist:fusing dist:include="true" />
        <dist:delivery>
            <dist:install-time />
        </dist:delivery>
    </dist:module>
 
</manifest>
  1. 解压 GameAssetsRes.APK,将 AndroidManifest.xmlresources.pb 按照附录的对照表复制到 GameAssetsRes 文件夹下对应位置。
  2. GameAssetsRes 文件夹压缩为 GameAssetsRes.zip

5. 构建 AAB

前面的步骤生成了 base.zipGameAssetsRes.zip,构建 AAB 需要使用 bundletool.jar,在 --modules 后加入两者的绝对路径,用 , 分隔开即可。

命令行:

java -jar [bundletool.jar ] build-bundle --modules [module的 zip 文件列表] --output=[输出的 aab]

6. 签名

最后就是对 AAB 进行签名,安卓的签名方式 v1jarsignerv2 是用 APKsigner,但是 AAB 只能通过 jarsigner 签名。签名之后最终的 AAB 就可以拿去上传 Google play 后台 了。

命令行:

jarsigner -digestalg SHA1 -sigalg SHA1withRSA -keystore [keystore 文件] -storepass [store password] -keypass [key password] [AAB文件] [store alias]

三、附录

对照表

源文件目标文件
base.APK/AndroidManifest.xml./base/manifest/AndroidManifest.xml
base.APK/res./base/root/res
base.APK/resources.pb./base/resources.pb
GameAssetsRes.APK/AndroidManifest.xml./GameAssetsRes/manifest/AndroidManifest.xml
GameAssetsRes.APK/resources.pb./GameAssetsRes/resources.pb
repackage/assets./base/assets(app配置文件和小文件放此处)、./GameAssetsRes/assets(大资源移动到此处)
repackage/lib./lib
repackage/kotlin./base/root/kotlin
repackage/unknown./base/root
repackage/original/META-INF./base/root/META-INF
repackage/smali、repackage/smali_classes2 ..../base/dex/classes.dex、./base/dex/classes2.dex ...(可以通过 smali.jar编译 smali为 dex,也可以直接用 APK 解压的 dex)

文件对应关系

  • repackage: 反编译目录
  • base.APK: 生成的资源 .apk
  • GameAssetsRes.APK: 分割的游戏资源 .apk
  • ./libAAB 目录中的 lib 文件夹
  • ./baseAAB 目录中的 base 文件夹
  • ./GameAssetsResAAB 目录中的 GameAssetsRes 文件夹