Tinker接入实践
关于Tinker
请出门右转,自己百度吧
接入tinker遇到的问题
首先说明一下使用的tinker版本以及项目AGP版本,tinker 1.9.9 + AGP 3.2.0
1. 主dex益出问题
1 | com.android.dex.DexException: Too many classes in --main-dex-list, main dex capacity exceeded |
此问题最根本的原因在主dex过大,也就是说需要放置到主dex的类过多。那么为什么接入tinker之后会发生这个问题呢?tinker全部的类也没有这么多啊?
原因
首先看一下系统默认的哪些类会放置到主dex。 最初非常古老的AGP(好像是2.3以下),会默认将application和AndroidManifest.xml中声明的类(四大组件)的直接引用类
放置到主dex。也正是这个原因,在老的AGP版本时,我们也层发生过主dex过大的问题。 不过好在后来AGP升级之后,google只是将一些系统启动时必要的类放到主dex。具体哪些是必要的类,我们可以查看一下AGP的原来来寻找答案(后续补充)。不过在我看来,只需要将application中MultiDex.install()执行前的引用类
全部放置到主dex即可。
了解这一点后,我们来看一下tinker 的com.tencent.tinker.patch plugin是如何处理分包的?
会直接操作multidexKeep文件,将application直接引用的类全部放置到主dex。
具体在plugin的TinkerMultidexConfigTask中将一下代码放置到分包规则中,由于ApplicationLike实现了ApplicationLifeCycle接口,直接导致ApplicationLike的所有类放置到了主dex(即引入tinker前所有的application中引用类放到了主dex)
1 | -keep public class com.tencent.tinker.entry.ApplicationLifeCycle { |
解决方案
- 修改源代码: 直接修改plugin源码将上述规则干掉即可。
- 不修改源代码: hook TinkerMultidexConfigTask此task,使其不执行,然后自己设置主dex的分包规则。
1 | //第一步: |
2. Java 8 language support 问题
问题描述
如果你直接使用了jdk 8,最小支持版本在5.0以下,AGP版本时3.2.0以上,并且tinker版本号大于1.9.9,那么默认情况下编译会提示一下错误
1 | Java 8 language support, as requested by 'android.enableD8.desugaring= true' in your gradle.properties file, is not supported when 'android.useDexArchive= false' |
问题原因
使用了java 8 之后默认编译的.class, 只能保证jvm8以及以上版本虚拟机正常运行,那就无法保证低版本android手机能正常执行了。
为了解决这一个问题,AGP默认要求必须启用D8的脱糖(desugaring)
, 其目的一句话描述就是修改.class文件,保证.class文件在低版本虚拟机上的兼容性。
因此,为了能够强制性保证apk在低版本手机上的运行,AGP会强制弹出此错误。
解决方法
有两个解决方式,如下:
- 按照错误提示,在gradle.properties文件中添加
android.useDexArchive= true
。 - 降低AGP版本到3.1.4以下。
特定项目发生的问题
1. Linux系统打出的apk包作为base包,在Mac OS上生成patch包体积非常大
问题背景
由于我们项目最终生产线上包时,使用的是一台Linux的云主机进行打包操作的; 而开发人员确是使用Mac进行开发工作的。因此,如果用线上包在本地生成patch包,则产生了此问题。
问题原因
AGP 打包时默认会对部分资源做一些特定优化(如会对png图片进行优化压缩), 这部分优化在不通系统,不同机器上可能导致产生的优化后文件不一致,因此做diff对比是会导致不一致,而认为资源发生了变化,顾会导致此问题。
解决方__案
解决此问题的关键在于查看异常的patch包和正常的patch包差异发生在哪儿?然后将差异进行消除,即不让AGP做这部分优化。
例如常见的:png的优化导致的差异如何解决
禁用png优化或者让tinker忽略png图片的对比
1 | aaptOptions{ |
Tinker合成Dex的优势
很多看完QQ空间访问的人会有疑问,QQ空间方案,只保留有变化的class,生成dex包,体积比较小,加载有很快,也不需要客户端独立进程合成新dex。Tinker为什么要大费周折的打包时进行diff操作,又在客户端进行merge操作呢?
其实原因很简单,QQ空间的方案是将class不进行CLASS_ISPREVERIFIED标记。但是此操作室友一定隐患和性能问题的。一旦修改了此标记,对类的加载其实是有效率问题,虽然没有明确测试影响有多大,但是原理上分析确实是有,而且是自主导致的,不可避免。
因此,为了能够从根源上杜绝这种问题的发生,tinker才大费周折的生成diff包,然后在客户端浪费一定时间和空间merge成完整dex。