一、微信Android热补丁方案
1、Tinker是什么
Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码、So库以及资源,让应用能够在不需要重新安装的情况下实现更新。当然,你也可以使用Tinker来更新你的插件。
它主要包括以下几个部分:
1.1、gradle编译插件: tinker-patch-gradle-plugin
1.2、核心sdk库: tinker-android-lib
1.3、非gradle编译用户的命令行版本: tinker-patch-cli.jar
2、为什么使用Tinker
当前市面的热补丁方案有很多,其中比较出名的有阿里的AndFix、美团的Robust以及QZone的超级补丁方案。
但它们都存在无法解决的问题,这也是正是我们推出Tinker的原因。
总的来说:
2.1、AndFix作为native解决方案,首先面临的是稳定性与兼容性问题,更重要的是它无法实现类替换,它是需要大量额外的开发成本的;
2.2、Robust兼容性与成功率较高,但是它与AndFix一样,无法新增变量与类只能用做的bugFix方案;
2.3、Qzone方案可以做到发布产品功能,但是它主要问题是插桩带来Dalvik的性能问题,
以及为了解决Art下内存地址问题而导致补丁包急速增大的。
特别是在Android N之后,由于混合编译的inline策略修改,对于市面上的各种方案都不太容易解决。
而Tinker热补丁方案不仅支持类、So以及资源的替换,它还是2.X-8.X(1.9.0以上支持8.X)的全平台支持。
利用Tinker我们不仅可以用做bugfix,甚至可以替代功能的发布。Tinker已运行在微信的数亿Android设备上,那么为什么你不使用Tinker呢?

3、Tinker的已知问题
由于原理与系统限制,Tinker有以下已知问题:
3.1、Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件(1.9.0支持新增非export的Activity);
3.2、由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码;
3.3、在Android N上,补丁对应用启动时间有轻微的影响;
3.4、不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed";
3.5、对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标。
4、Tinker 获取的信息
Tinker 在运行时只获取必要的信息以保证补丁的合成和加载流程能在不同厂商、操作系统版本的设备上正常执行,
这些信息不涉及系统敏感权限和用户隐私相关的内容。同时 Tinker 不含上报逻辑,即不会上报获取到的信息。
有关 Tinker 获取信息的类型及用途,请查看 Tinker SDK 个人信息保护规则 了解详情
5、如何使用Tinker
Tinker为了实现“高可用”的目标,在接入成本上做了妥协。热补丁并不简单,在使用之前请务必先仔细阅读以下文档:
6、Tinker的TODO
Tinker经过几次全量上线,也发现了一些热补丁的问题。有以下的一些优化工作尚未完成:
6.1、Crash 启动保护;
6.2、Android Q 或更新的系统上降低补丁带来的开销。
二、Tinker API概览
Tinker API概览
我们需要使用的API大约几种在以下几个类中:

1、TinkerInstaller相关接口
TinkerInstaller封装了一些比较重要的函数,现作简单的说明:
1.1、Tinker实例的构建
Tinker类是整个框架的核心,我们需要构建它的单例。其中intentResult存放是我们加载补丁时的数据,
它在install之后才将数据赋值给Tinker与TinkerLoadResult类。
全部使用默认定义类的构造方法:
public static void install(ApplicationLike applicationLike) {
Tinker.with(tinkerApplication).install(applicationLike.getTinkerResultIntent());
}
若使用了自定义类,可选择多参数的install方法,具体用法可参考SampleApplicationLike。
你一定要先install Tinker之后,才能使用Tinker相关的API。不然你只能使用TinkerApplicationHelper.java中的API。
1.2、发起补丁修复请求
正如之前所说的,所有的补丁升级请求都将会分发到PatchListener去处理。
发起升级补丁请求,即收到一个新的补丁包,多次补丁也是调用下面这个接口:
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
1.3、Library库的加载
不使用Hack的方式
更新的Library库文件我们帮你保存在tinker下面的子目录下,但是我们并没有为你区分abi(部分手机判断不准确)。
所以若想加载最新的库,你有两种方法,第一个是直接尝试去Tinker更新的库文件中加载,第二个参数是库文件相对安装包的路径。
TinkerLoadLibrary.loadLibraryFromTinker(getApplicationContext(), "assets/x86", "libstlport_shared");
但是我们更推荐的是,使用TinkerInstaller.loadLibrary接管你所有的库加载,
它会自动先尝试去Tinker中的库文件中加载,但是需要注意的是当前这种方法只支持lib/armeabi目录下的库文件!
//load lib/armeabi library
TinkerLoadLibrary.loadArmLibrary(getApplicationContext(), "libstlport_shared");
//load lib/armeabi-v7a library
TinkerLoadLibrary.loadArmV7Library(getApplicationContext(), "libstlport_shared");
若想对第三方代码的库文件更新,可先使用TinkerLoadLibrary.load*Library对第三方库做提前的加载!更多使用方法可参考MainActivity.java。
使用Hack的方式
以上使用方式似乎并不能做到开发者透明,这是因为我们想尽量少的去hook系统框架减少兼容性的问题。
Tinker也提供了一键反射Library Path的方式供大家选择:
// 将tinker library中的armeabi注册到系统的library path中。
TinkerLoadLibrary.installNavitveLibraryABI(context, "armeabi");
当然,当前手机系统的abi 需要大家自行判断传入即可,这样我们就无需再对library的加载做任何的介入。
1.4、设置LogIml实现
你可以设置自己的Log输出实现:
public static void setLogIml(TinkerLog.LogImp imp) {
TinkerLog.setLogImp(imp);
}
2、Tinker类相关接口
Tinker类是整个库的核心,基本所有的补丁相关的信息我们都可以在这里获取到。
TinkerApplication也有一些获取tinkerFlag的相关API,它们可以在Tinker未被初始化前使用。
2.1、获取单例的方法
Tinker manager = Tinker.with(context);
2.2、获取加载状态
loaded成员变量是标记是否有补丁加载成功的标记,只有它为true时,才能保证TinkerLoadResult的各个变量非空。
boolean isLoaded = Tinker.with(context).isTinkerLoaded();
boolean isInstalled = Tinker.with(context).isTinkerInstalled();
3531

被折叠的 条评论
为什么被折叠?



