前言
本文最主要的是讲解下自己是如何在在项目中更恰当合理的运用SopHix,当然这只是我团队两个人的做法而已。经验总是要踩出来的,对同学有帮助就最好了。
以下分为六部分
-
引言
-
官方使用步骤
-
测试开发中热更新修复操作五部曲
-
下发补丁步骤
-
如何处理加载补丁成功后的弹窗强制更新提醒
-
如何爬坑
一、引言
热修复技术可谓是百花齐放,许多产品都声称自己可以做到全方位全功能的热修复。不过他们各自有自身的局限性,或者不够稳定,或者补丁过大,或者效率低下,或者使用起来过于繁琐,大部分技术上看起来似乎可行,但实际体验并不好。而在我们看来,有很多技术细节能够做得更加完美。
终于在2017年6月11日,手淘技术团队联合阿里云正式发布了史上首个非侵入式移动热更新解决方案——Sophix。
Sophix热修复方案主要开发者,万壑 就职于手机淘宝。
Sophix的横空出世,将会打破各家热修复技术纷争的局面。我们可以满怀信心地说,在Android热修复的三大领域:代码修复、资源修复、so修复方面,以及方案的安全性和易用性方面,Sophix都做到了业界领先。
文章介绍 https://yq.aliyun.com/articles/103527?spm=5176.8091938.0.0.l6pz1T
下面的这张表格引用自[ 作者博文],从几个热修复最重要的维度,把Sophix和另外两个主要商业化热修复方案进行了比较。
而其中唯一不支持的地方就是四大组件的修复,这是因为如果要修复四大组件,必须在AndroidManifest里面预先插入代理组件,并且尽可能声明所有权限,而这么做就会给原先的app添加很多臃肿的代码,对app运行流程的侵入性很强。可以插入几个代理文件即可。我在项目中就这么做。
二、官方使用步骤
Android 接入流程请开发者务必仔细阅读一遍官方文档。文档是最好的老师
先去创建应用
然后快速接入流程
三、测试开发中热更新修复操作五部曲
打包区分的三个包
Base_1.5.30 (基础包用于对比)
Safe_1.5.30(加固过后的包,发布给线上使用)
Test_1.5.31(在打完Base包之后修改版本号,再打一个测试包,确保代码一致,用于安装,下发补丁首先下发到该版本,我是这么做的)Safe_1.5.30发布完上线之后 ,在你的Android Studio中修改版本号为1.5.33 继续开发或者修复BUG,反正需要大于1.5.31(因为你做测试需要先在Sophix后台下发补丁到1.5.31,你本地代码没有修改的话, build 运行时会收到Sophix服务器的下发的补丁,此时代码覆盖会让你一脸懵逼)
这时你已经在 Dev_1.5.33 版本中修复了BUG,打包1.5.33之后,需要通过 Sophix工具上传 基础包 Base_1.5.30 进行比对生成 Sophix-Patch.jar 补丁包 ,(生成的补丁与版本号无关,工具只会对比代码结构, so,资源等) 完成后将其上传至Sophix后台
指定下发对应版本号,先下发到Test_1.5.31 测试版本,补丁下发成功! 手机安装 先测试验收,验收成功之后再指定下发到 正式的版本 1.5.30 即可
Tips: 在Android Studio中 开发中的版本需要比Test1.5.31版本大,便于在 Build 运行项目时,不会因联网下发到针对1.5.31的补丁 指示代码收到下发的补丁 受影响
注意事项:
下发补丁修复bug不要去修改 Manifest 的文件,发布补丁时的版本也最好不要去修改一些依赖库的版本,会出现某些问题!
还有打base包的 那份代码需要备份这个分支,然后copy一个分支进行下一步的开发,以防万一合成补丁出错,进行代码比对
旧包内部版本号不能 比新包内部版本号大,不然会生成补丁出错
这里我并没有使用官方的调试工具进行调试。而是直接在项目中查看日志补丁加载状态。要多注意每个Code代表的意思 ,这里文档也有描述到,详情查看SDK中PatchStatus类的代码,有具体说明
四、下发补丁步骤
生成补丁工具
假设当前 版本为 1.5.30 为发布的包,而你要先进行更新测试包更新包版本为 1.5.31 ,这个时候你就要将最新代码生成的apk文件包与base基线版本1.5.30做比对。此时生成的补丁文件 sophix-patch.jar 上传至 Sophix 后台。
补丁版本为后台自控的。会进行叠加。继续上传一个补丁则上一个版本的补丁自动停用.
- 添加版本号要注意格式1.X.X 或者 1.XXX
![~J$E2{$(6OFRBSI]WA7Y0MR.png](http://upload-images.jianshu.io/upload_images/956862-2a3012b0a3c770ab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -
上传补丁文件,并且加点描述
五、如何处理加载补丁成功后的弹窗强制更新提醒
- 强制更新补丁状态说明:
一般都会把查询补丁的操作放在Application中去操作,Sophix启动APP时会调用方法请求查询最新的补丁,这句话一般在Application中的onCreate()方法的Super之前去进行调用,这里贴一下自己项目中的代码
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
initHotfix(base);
}
public void onCreate() {
SophixManager.getInstance().queryAndLoadNewPatch();
super.onCreate();
...
}
// 阿里热修复
private void initHotfix(Context context) {
SophixManager.getInstance().setContext(this)
.setAppVersion(VersionUtils.getVersionName(this))
.setAesKey(null)
.setEnableDebug(false)
.setPatchLoadStatusStub((mode, code, info, handlePatchVersion) -> {
// 补丁加载回调通知
String s = "mode:" + mode + " code:" + code + "\npatchVersion:" + handlePatchVersion + "\ninfo:" + info;
hotfixController(context, code, handlePatchVersion, s);//这里是处理相对应的主要Code的操作,代码就不贴完整了,你可以根据官方的来进行操作,建议多加一个对CODE_LOAD_NOPATCH 状态码14,没有补丁的情况针对性的操作
}).initialize();
}
```
Q:收到Code 12 之后,有的包是需要提示用户强制冷启动的,而有的包是不用提示,等下次自己更新就好了。这样的情况怎么判断当前的包是处于哪种情况呢?
需要提示 & 不需要再提示?
A:官方给我的提示,需要自己加个推送什么的,回调中没有这个标识。((__) 嘻嘻…… 如果Shophix后台有支持就好了)这里主要讲解下发成功 状态为 1 "load new patch success."时,也就是补丁下发成功了,之后提醒用户的操作。
这里需要判断两个前提条件,都要提示用户去重启应用, 要注意回调状态码Code 等于12 “please relaunch app to load new patch” 的时候 就是冷启动的提示。 这里我并没有根据12去做判断进行重启。
下面说下我自己项目中的两个必要重启判断条件,仅供参考。
* 1、取得SopHix SDK启动时返回的最新的补丁版本号(hotfixPatchVersion)和服务器返回的补丁版本号(hotfixMustUpdateVersion)相同,且大于当前最后一次强制重启的补丁版本号(hotfixLastMustVersion 将每次返回的补丁版本写入SP记录)
* 2、或者第一次下载、数据被清除后,如果有新强制更新版本,则强制更新
建议定义三个字段进行比较
public static final String hotfixPatchVersion = "hotfixPatchVersion"; // 阿里返回的补丁版本号
public static final String hotfixMustUpdateVersion = "hotfixMustUpdateVersion"; // 服务器返回的版本号
public static final String hotfixLastMustVersion = "hotfixLastMustVersion"; // 最后一次强制重启的版本号
注意:
以上所说的这种方式,实际项目中加载了最新的补丁,需要把这个Sophix中最新的补丁版本号,告知后台进行修改为最新更新的补丁号。一般在版本更新的接口中多加入一个字段(downloadPatchCode ),请求时用于进行比对。
提示用户关闭时的处理,可以在PatchLoadStatusListener监听到CODE_LOAD_RELAUNCH后在合适的时机,调用此方法杀死进程。注意,不可以直接Process.killProcess(Process.myPid())来杀进程,这样会扰乱Sophix的内部状态。因此如果需要杀死进程,建议使用这个方法,它在内部做一些适当处理后才杀死本进程。本人是直接在一个透明的 弹窗提示判断中关闭时调用的此方法进行关闭。
SophixManager.getInstance().killProcessSafely();
六、如何爬坑
以下是我所遇到过的两个问题,遇到问题心态不要蹦,先去查看日志,再去查看官方文档,或者QA,百分之七八十都可以解决,若你还不能解决你就去官方钉钉群交流:11734260 进行交流。
问题1
热修复Android SDK:在4.x的手机系统上崩溃
在4.x的系统上出现IllegalAccessException:class ref in pre-verified异常,应用崩溃
问题原因
这个是由于我们是完整dex修复,所以会出现新dex中的类和老dex中的类冲突,所以要在Sophix初始化前避免加载原有apk中的类,而对于4.x版本的系统,如果在Sophix初始化之前有加载原有apk中的类,则会影响修复的过程,造成崩溃
解决方案
https://help.aliyun.com/knowledge_detail/55414.html?spm=5176.7851422.2.4.SyAVhp
Sophix初始化在Application最前面,同时尽量用系统类及尽量不使用log等,或者把initialize写到attachBaseContext里面,但query还是在onCreat的最前面;如果有用到MultiDex,直接继承Application,在attachBaseContext里写MultiDex.install(base),然后在onCreat的最前面initialize和query,如果还会出现这个崩溃,把initialize写到attachBaseContext的MultiDex.install(base)后面,但query还是在onCreat的最前面
问题2
这个问题很奇怪,项目老大解决的。我现在只知道不能这么做而已。
在4.X的设备上,用OkHttp作为Fresco的网络加载器的时候,如果发布新的包(比如只修改一个Toast),
在下载完重启后会直接崩溃报如下错误:
Process: com.newtest.myapplication, PID: java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
而其他机型则不会。 很明显和第一个错误是一样的。
Sophix对4.X的机型有Bug: 在一个外部引用的包里,自带的实例,需要去调用这个包里的另一个实例方法时,会报上面这个错; 但把这个包里的实例移到App里创建,再去调用相同的那个方法里就不会了。 就是说4.X的引用包里的方法不能再次调用引用包里的别的方法。 现在已经解决了,但5.0及以上的机型却不会有这个Bug,这只是很奇葩的问题,如果没遇到过同学略过吧~
以上两个问题也属于同一个问题,详细阅读作者这边文章会有些启示。
- 由于是直接在项目分支中引入的Sophix 所以没有写个Demo。
有空补上(这是我听过过最大的笑话)
贴一个官方Demo
以上仅是自己的处理方式,写的不好的地方还望指正,有问题还望同学们指点一二,O(∩_∩)O谢谢!
学会分享~