概述
在gradle升级到4.0.2时遇到了release包构建成功,并且在构建过程中无异常提示,但是百度地图BaiduLBS_Android.jar中assets下文件丢失,导致地图相应功能丢失,app崩溃情况。在解决问题的过程中,耗费时间较长,一度以为是混淆问题,因为debug包没问题,并且release在打开R8的情况下,也可以解决,gradle升级到4.1也可以解决。但是具体原因是什么呢?到底是混淆问题,还是其他原因,带着这个问题我们分析下第三方jar包assets下资源文件是如何打包到apk中的。
总流程
第三方jar包assets下资源文件构建总流程如下:
主要是分为以下几步:
- jar包中的assets下资源文件在生成aar后,不是在aar的assets目录下,而是在classed.jar的assets目录下,同时aar中libs下的jar包,不在包含assets目录,只有源码目录文件。
- 在构建过程中,通过mergeReleaseJavaResource合并java资源Task解析classes.jar并生成out.jar文件,out.jar中包含所有的非res文件,以及源码目录非claaes文件。例如:out/com/baidu/pano/platform/res/indoor_in.png
- 在minifyReleaseWithProguard的Task中,out.jar会被解析重新生成一个minified.jar文件,这个minified.jar文件不仅包含了out.jar内容,也包含了所有的classes文件。
- 在最后打包packageRelease的Task中,读取minified.jar文件中的assets目录文件,直接add到apk中。
PackageRelease 分析
先看下如下构建时序图:
- run:主要读取文件信息,manifest、dex、javaRes、assets、res、so、……等等。
- updateFiles:更新所有存档中所有新的、更改的和删除的文件,也就是dex、android res、java res、assets、so等等。
- addFiles:写入andoid 资源文件,和java资源文件。
在最后一步我们看到有两种文件处理方式:那么4.0版本我们用的是ApkZFileCreator,4.1版本我们用的是ApkFlinger。我们还可以通过设置android.useNewApkCreator配置决定使用哪个打包工具,值为true用ApkFlinger,否则用ApkZFileCreator,并且android.useNewApkCreator默认值为true,那么我们在4.0版本应该使用的是否则用ApkZFileCreator,然后我们跟踪一下4.0源码:
1 | if (!apkFormatIsFile || !debuggableBuild) { |
我们可以发现,无论我们如何,release构建都是用的ApkZFileCreator,debug用的是ApkFlinger。然后我们在看一下4.1源码:
1 | if (!apkFormatIsFile) { |
我们可以发现,无论我们如何,release构建都是用的ApkFlinger。
通过以上比较,和通过4.1进行构建打包,发现4.1是正常的,所以我们定位到4.0构建打包jar下assets目录文件和使用ApkZFileCreator文件有关。
ApkZFileCreator分析
下面的源码片段展示了写入的主要逻辑,分为如下 3 步:
- 创建 ZFile 对象,读取 zip 文件将 central directory 中的每项加入到 entries 中;
- 遍历 ZFile 中的 entries,将压缩的资源文件合并到 APK 文件中;
- 遍历 ZFile 中的 entries,将非压缩的资源文件写入到 APK 文件中;
ApkZFileCreator:
1 | public void writeZip(File zip, @Nullable Function<String, String> transform, @Nullable Predicate<String> isIgnored) throws IOException { |
ZFile.java
1 | private void readData() throws IOException { |
在调试过程中发现读取minified.jar文件创建的ZFile中的entries中文件读取不全,没有 Java 资源文件,而在前面 IncrementalSplitterRunnable.execute 中调 PackageAndroidArtifact.getChangedJavaResources获取改变的Java资源文件时,使用ZipCentralDirectory能正常读取到Java资源文件,由此说明ZFile存在缺陷。图示如下:
ZipCentralDirectory读取minified.jar:
ZFile读取minified.jar:
ZipCentralDirectory读取minified.jar:获取changeJavaResources文件数是101377,
ZFile读取minified.jar:获取entries文件数是35841,
两种方式读取的文件数完全不一样。
而且ZFile注释中所述,它不是通用的 zip 工具类,对 zip 格式和不支持的特性有严格的要求;它在某些特殊条件下存在限制,可能会出现读取文件缺失等问题
1 | * <p>Because {@code ZFile} was designed to be used in a build system and not as general-purpose zip |
那么为啥开启R8就没问题了呢?看一下R8的流程图:可以看到开启R8后,生成的java 资源文件是shrunkJavaRes.jar,这个shrunkJavaRes.jar文件和minified.jar相比仅仅包含了out.jar内容,不包含classes文件。
总结
- 4.0版本构建release包不管如何设置打包方式,都是执行的ApkZFileCreator;
- 4.0版本构建debug包,执行的新打包方式ApkFlinger;
- 4.1版本构建release包,可以通过android.useNewApkCreator设置打包方式(ApkZFileCreator/ApkFlinger),默认为true,执行ApkFlinger;
- 4.0版本构建release包虽然打包方式是ApkZFileCreator,但是经过了R8混淆,生成的java资源文件shrunkJavaRes.jar,并且此文件和minified.jar相比仅仅包含了out.jar内容,不包含classes文件;
参考
Ursprünglicher Link: http://nunu03.github.io/2021/11/15/Gradle-4-0-jar包assets资源丢失原因分析/
Copyright-Erklärung: 转载请注明出处.