热修复技术可谓是百花齐放

1. 简述

热修复技术可谓是百花齐放,微信的Tinker、QQ空间的Nuwa、饿了么的Amigo、美团的Robust等等,各个热修复方案争相发布,都声称自己可以做到全方位全功能的热修复。不过他们各自有自身的局限性,或者不够稳定,或者补丁过大,或者效率低下,或者使用起来过于繁琐,大部分技术上看起来似乎可行,但实际体验并不好。

在2017年6月11日,手淘技术团队联合阿里云正式发布了Android移动热修复方案——Sophix。Sophix的核心设计理念,就是非侵入性。Sophix这个名字,是来源于Sophic(明智的)+ FIX,一个更明智的热修复方案。

Sophix的横空出世,将会打破各家热修复技术纷争的局面。我们可以满怀信心地说,在Android热修复的三大领域:代码修复、资源修复、so修复方面,以及方案的安全性和易用性方面,Sophix都做到了业界领先。

2.优势

2.1 横向比较

方案对比 Sophix Tinker Amigo
DEX修复 同时支持即时生效和冷启动修复 冷启动修复 冷启动修复
资源更新 差量包,不用合成 差量包,需要合成 全量包,不用合成
SO库更新 插桩实现,开发透明 替换接口,开发不透明 插桩实现,开发透明
性能损耗 低,仅冷启动情况下有些损耗 高,有合成操作 低,全量替换
四大组件 不能新增 不能新增 能新增
生成补丁 直接选择已经编好的新旧包在本地生成 编译新包时设置基线包 上传完整新包到服务端
补丁大小
接入成本 傻瓜式接入 复杂 一般
Android版本 全部支持 全部支持 全部支持
安全机制 加密传输及签名校验 加密传输及签名校验 加密传输及签名校验
服务端支持 支持服务端控制 支持服务端控制 支持服务端控制

可以看到,Sophix在各个指标上全面占优。而其中唯一支持不完善的地方就是四大组件,四大组件可以修改代码,但是无法做到新增。这是因为如果要新增四大组件,必须在AndroidManifest里面预先插入代理组件,并且尽可能声明所有权限,而这么做就会给原先的app添加很多臃肿的代码,对app运行流程的侵入性很强,所以,本着对开发者透明与代码极简的原则,它没有做这种多余的处理。

2.2 纵向比较

方案对比 Andfix开源版本 阿里Hotfix 1.X 阿里Hotfix最新版 (Sophix)
方法替换 支持,除部分情况[0] 支持,除部分情况 全部支持
方法增加减少 不支持 不支持 以冷启动方式支持[1]
方法反射调用 只支持静态方法 只支持静态方法 以冷启动方式支持
即时生效 支持 支持 视情况支持[2]
多DEX 不支持 支持 支持
资源更新 不支持 不支持 支持
so库更新 不支持 不支持 支持
Android版本 支持2.3~7.0 支持2.3~6.0 全部支持包含7.0以上
已有机型 大部分支持[3] 大部分支持 全部支持
安全机制 加密传输及签名校验 加密传输及签名校验
性能损耗 低,几乎无损耗 低,几乎无损耗 低,仅冷启动情况下有些损耗
生成补丁 繁琐,命令行操作 繁琐,命令行操作 便捷,图形化界面
补丁大小 不大,仅变动的类 小,仅变动的方法 不大,仅变动的资源和代码[4]
服务端支持 支持服务端控制[5] 支持服务端控制

说明:

  • [0] 部分情况指的是构造方法、参数数目大于8或者参数包括long,double,float基本类型的方法。
  • [1] 冷启动方式,指的是需要重启app在下次启动时才能生效。
  • [2] 对于Andfix及Hotfix 1.X能够支持的代码变动情况,都能做到即时生效。而对于其他代码变动较大的情况,会走冷启动方式,此时就无法做到即时生效。
  • [3] Hotfix 1.X已经支持绝大部分主流手机,只是在X86设备以及修改了虚拟机底层结构的ROM上不支持。
  • [4] 由于支持了资源和库,如果有这些方面的更新,就会导致的补丁变大一些,这个是很正常的。并且由于只包含差异的部分,所以补丁已经是最大程度的小了。
  • [5] 提供服务端的补丁发布和停发、版本控制和灰度功能,存储开发者上传的补丁包。

3. 技术突破

3.1 原理(双剑合璧)

image

3.2 优化Andfix(突破底层结构差异,解决稳定性问题)

Andfix底层ArtMethod结构时采用内部变量一一替换,倒是这个各个厂商是会修改的,所以兼容性不好。

Sophix改变了一下思路,采用整体替换方法结构,忽略底层实现,从而解决兼容稳定性问题。

这么一来,不仅解决了兼容性问题,并且由于忽略了底层ArtMethod结构的差异,对于所有的Android版本都不再需要区分,代码量大大减少。即使以后的Android版本不断修改ArtMethod的成员,只要保证ArtMethod数组仍是以线性结构排列,就能直接适用于将来的Android 9.0、9.1等新版本,无需再针对新的系统版本进行适配了。事实也证明确实如此,当拿到Google刚发不久的Android P(9.0)开发者预览版的系统时,hotfix demo直接就能顺利地加载补丁跑起来了,我们并没有做任何适配工作,鲁棒性极好。

3.3 突破QQ和Tinker的缺陷

QZone Tinker
原理 为了解决Dalvik下unexpected dex problem异常而采用插桩的方式,单独放一个帮助类在独立的dex中让其他类调用,阻止了类被打上CLASS_ISPREVERIFIED标志从而规避问题的出现。最后加载补丁dex得到dexFile对象作为参数构建一个Element对象插入到dex-Elements数组的最前面。 提供dex差量包,整体替换dex的方案。差量的方式给出patch.dex,然后将path.dex与应用的classes.dex合并成一个完整的dex,完整dex加载得到的dexFile对象作为菜蔬构建一个Element对象然后整体替换掉旧的dex-Elements数组。
优点 没有合成整包,patch比较小,比较灵活 自研dex差异算法,path包很小,dex merge成完整的dex,Dalvik不影响类加载性能,Art下也不存在必须包含父类/引用类的情况
缺点 Dalvik下影响类的加载性能,Art下类地址写死导致必须包含父类/引用,最后patch包很大 Dex合并内存消耗在VM heap上,容易OOM,最终导致dex合并失败

Sophix对dex的解决方案

  • Dalvik下采用阿里自研的全量dex方案:不是考虑把补丁包的dex插到所有dex前面(dex插桩),而是想办法在原理的dex中删除(只是删除了类的定义)补丁dex中存在的类,这样让系统查找类的时候在原来的dex中找不到,那么只有补丁中的dex加载到系统中,系统自然就会从补丁包中找到对应的类。
  • Art下本质上虚拟机以及支持多dex的加载,Sophix的做法仅仅是把补丁dex作为主dex(classes.dex)而已,相当于重新组织了所有的dex文件:把补丁包的dex改名为classes.dex,以前apk的所有dex依次改为classes2.dex、classes3.dex … classesx.dex。

dex的merge图解:

3.3 资源修复另辟蹊径

常用方案(Instant Run技术):这种方案的兼容问题在于替换AssetManager的地方

目前市面上的很多资源热修复方案基本上都是参考了Instant Run的实现。实际上,Instant Run的推出正是推动这次热修复浪潮的主因,各家热修复方案,在代码、资源等方面的实现,很大程度上地参考了Instant Run的代码,而资源修复方案正是被拿来用到最多的地方。

简要说来,Instant Run中的资源热修复分为两步:

  1. 构造一个新的AssetManager,并通过反射调用addAssetPath,把这个完整的新资源包加入到AssetManager中。这样就得到了一个含有所有新资源的AssetManager。
  2. 找到所有之前引用到原有AssetManager的地方,通过反射,把引用处替换为AssetManager。

其实大量代码都是在处理兼容性问题和找到所有AssetManager的引用处,真正的替换的逻辑其实很简单。

Sophix资源修复方案

Sophix方案没有直接使用Instant Run的技术,而是另辟蹊径,构造了一个package id为0x66的资源包,这个包里只包含改变了的资源项,然后直接在原有AssetManager中addAssetPath这个包就可以了。由于补丁包的package id为0x66,不与目前已经加载的0x7f冲突,因此直接加入到已有的AssetManager中就可以直接使用了。补丁包里面的资源,只包含原有包里面没有而新的包里面有的新增资源,以及原有内容发生了改变的资源。并且,我们采用了更加优雅的替换方式,直接在原有的AssetManager对象上进行析构和重构,这样所有原先对AssetManager对象的引用是没有发生改变的,所以就不需要像Instant Run那样进行繁琐的修改了。

可以说,Sophix的资源修复方案,优越性超过了Google官方的Instant Run方案。整个资源替换的方案优势在于:

  1. 不修改AssetManager的引用处,替换更快更完全。(对比Instanat Run以及所有copycat的实现)
  2. 不必下发完整包,补丁包中只包含有变动的资源。(对比Instanat Run、Amigo等方式的实现)
  3. 不需要在运行时合成完整包。不占用运行时计算和内存资源。(对比Tinker的实现)

所以,我们不要被所谓的“官方实现”束缚住手脚,其实Instant Run的开发团队和Android framework的开发团队并不是同一个团队,他们对于Android系统机制的理解未必十分深入。

3.4 SO修复另辟蹊径

so库的修复本质上是对native方法的修复和替换。

我们知道JNI编程中,native方法可以通过动态注册和静态注册两种方式进行。动态注册的native方法必须实现JNI_OnLoad方法,同时实现一个JNINativeMethod[]数组,静态注册的native方法必须是Java+类完整路径+方法名的格式。

动态注册的native方法映射通过加载so库过程中调用JNI_OnLoad方法调用完成,静态注册的native方法映射是在该native方法第一次执行的时候才完成映射,当然前提是该so库已经load过。

sophix采用的是类似类修复反射注入方式。把补丁so库的路径插入到nativeLibraryDirectories数组的最前面,就能够达到加载so库的时候是补丁so库,而不是原来so库的目录,从而达到修复的目的。

采用这种方案,完全由Sophix在启动期间反射注入patch中的so库。对开发者依然是透明的。不用像某些其他方案需要手动替换系统的System.load来实现替换目的。

4. 项目集成

移动热修复(Mobile Hotfix),阿里云有详细的接入文档,按照文档一步一步来,很容易就实现了。

5. 集成问题

5.1 检查到稳健接入初始化错误

":checkApplication FAILED\r"
"BUILD FAILED\r"
"java.lang.RuntimeException: (检查到稳健接入初始化错误<init error 501>)-> Sophix Stub Application类中不得使用非Android SDK的类: [Lap;]\r"
"\tat com.taobao.sophix.c.j.a(PreCheckApplication.java:110)\r"
"\tat com.taobao.sophix.c.e.a(PatchCommand.java:247)\r"
"\tat com.taobao.sophix.Main.main(Main.java:34)\r"

解决方法:
SophixPatchTool工具的打开高级设置->不勾选初始化检查

结尾

谢谢大家看完,如有不恰当、不充分的地方,欢迎大家指正。针对这块知识点我在学习过程中,进行了详细的整理梳理了一些学习笔记,有需要参考学习的小伙伴可以 点击这里查看获取方式 传送门直达 !!!



原文地址:https://blog.csdn.net/weixin_61845324

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


更新Android SDK到3.0版本时,遇到Failed to rename directory E:\android\tools to E:\android\temp\ToolPackage.old01问题,导致无法更新,出现该问题的原因是由于3.0版本与较早的sdk版本之间文件结构有冲突,解决
Android 如何解决dialog弹出时无法捕捉Activity的back事件 在一些情况下,我们需要捕捉back键事件,然后在捕捉到的事件里写入我们需要进行的处理,通常可以采用下面三种办法捕捉到back事件: 1)重写onKeyDown或者onKeyUp方法 2)重写onBackPressed方
Android实现自定义带文字和图片的Button 在Android开发中经常会需要用到带文字和图片的button,下面来讲解一下常用的实现办法。一.用系统自带的Button实现 最简单的一种办法就是利用系统自带的Button来实现,这种方式代码量最小。在Button的属性中有一个是drawable
Android中的&quot;Unable to start activity ComponentInfo&quot;的错误 最近在做一款音乐播放器的时候,然后在调试的过程中发现一直报这个错误&quot;Unable to start activity ComponentInfo&quot;,从字面
Android 关于长按back键退出应用程序的实现最近在做一个Android上的应用,碰到一个问题就是如何实现长按back键退出应用程序。在网上查找了很多资料,发现几乎没有这样的实现,大部分在处理时是双击back键来退出应用程序。参考了一下双击back键退出应用程序的代码,网上主流的一种方法是下面
android自带的时间选择器只能精确到分,但是对于某些应用要求选择的时间精确到秒级,此时只有自定义去实现这样的时间选择器了。下面介绍一个可以精确到秒级的时间选择器。 先上效果图: 下面是工程目录: 这个控件我也是用的别人的,好像是一个老外写的,com.wheel中的WheelView是滑动控件的主
Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing
Android ListView的item背景色设置以及item点击无响应等相关问题 在Android开发中,listview控件是非常常用的控件,在大多数情况下,大家都会改掉listview的item默认的外观,下面讲解以下在使用listview时最常见的几个问题。1.如何改变item的背景色和按
如何向Android模拟器中导入含有中文名称的文件在进行Android开发的时候,如果需要向Android模拟器中导入文件进行测试,通过DDMS下手动导入或者在命令行下通过adb push命令是无法导入含有中文文件名的文件的。后来发现借用其他工具可以向模拟器中导入中文名称的文件,这个工具就是Ultr
Windows 下搭建Android开发环境一.下载并安装JDK版本要求JDK1.6+,下载JDK成功后进行安装,安装好后进行环境变量的配置【我的电脑】-——&gt;【属性】——&gt;【高级】 ——&gt;【环境变量】——&gt;【系统变量】中点击【新建】:变量名:CLASSPATH变量值:……
如何利用PopupWindow实现弹出菜单并解决焦点获取以及与软键盘冲突问题 在android中有时候可能要实现一个底部弹出菜单,此时可以考虑用PopupWindow来实现。下面就来介绍一下如何使用PopupWindow实现一个弹出窗。 主Activity代码:public void onCreat
解决Android中的ERROR: the user data image is used by another emulator. aborting的方法 今天调试代码的时候,突然出现这个错误,折腾了很久没有解决。最后在google上找到了大家给出的两种解决方案,下面给出这两种方法的链接博客:ht
AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;RelativeLayout ="http://schemas..._android studio 界面跳转
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具