故事的开头就从一个问题开始:
为何andfix能即时生效?
在app运行到一半的时候,所有需要发生变更的分类已经被加载过了,在android上是无法对一个分类进行卸载的。而腾讯系的方案,都是让classloader去加载新的类。如果不重启,原来的类还在虚拟机中,就无法加载新的类。因此,只有下次重启的时候,在还没走到业务逻辑之前抢先加载补丁中的新类,这样后续访问这个类时,就会resolve微信类。达到热修复目的。
andfix,在已经加载的类中直接在native层替换掉原有的方法,是在原来类的基础上进行修改。
很多手机不支持,是因为他替换原有的方法失败或者不成功。因为他的native method的方法没有精准的反射到。(各场商的rom 会有所修改)
sopix支持两套方案一套即时生效的andfix方案,第二种冷启动机制 ,类似于tinker的方案。
我选择了后者。。。。
1 在项目外的gradle进行配置classpath "com.tencent.tinker:tinker-patch-gradle-plugin:1.7.11"
2.这三个配置文件都在app的gradle下
multiDexKeepProguard file("keep\_in\_main\_dex.txt”)
dxy\_hotfix : 'cn.dxy.library:hotfix:0.4.1’,
tinker\_anno :"com.tencent.tinker:tinker-android-anno:1.7.11",
3.最后,代码里面:替换为application。(app的 xxxApplication -> xxxApplicationLike)application里面的代码不用改变。@ DefaultLifeCycle里面的application路径记得替换成自己app包下面的。这个application编译时生成!
@DefaultLifeCycle(application = (自己包下面application路径)"cn.xxx.xxxApplication",
flags = ShareConstants.TINKER\_ENABLE\_ALL)
public class xxxApplicationLike extends DefaultApplicationLike
app初始化时调用 :PatchManager.get().tryPatch();//请求patch包下载
app双击退出应用结束调用:PatchManager.exitApp(getApplication());//退出
4.文件:keep_in_main_dex.txt hotfix.gradle
后台接口的设计
问题:如果A用户用1.0.0版本的APK,B用户用2.0.0版本的APK,这个时候1.0.0和2.0.0都有对应的补丁包。接口该怎么设计?
方案: (可以保证用1.0.0是2.0.0的用户都可以修复)
叫后台给一个接口,前端传versionName给后台(这里的versionName要保证和TinkerID一样), 传1.0.0后台就返回1.0.0的补丁包。传2.0.0后台则返回2.0.0的补丁包。字段后台返回一个补丁包的链接就可以了. 每次更新补丁包后台都要换不同的链接。没有则返回空。
前端设计与问题
问题: 前端下载APK的时机和逻辑
方案: 放在启动页-SplashActivity请求比较好(越早请求越好),每次都去请求,把请求回来的链接保存在本地,进行对比,链接不一样则下载补丁包并加载。连接一样则不用重复下载。
问题: 前端下载的时候需不需要提示用户?
方案: 这个看产品经理的需求,一般可以不提示,我修复bug告诉你干嘛…
问题: 如果1.0.0版本上线后,过了很久才发现有bug, 我的trunk主线代码已经改了很多了。这个时候打补丁包那不是把其他代码也认为是差异的代码,然后直接加载补丁包到1.0.0的apk上?这样不合理吧?
方案:
发布1.0.0版本后, 新建一个1.0.0的分支, 然后在1.0.0分支上修改bug,打出补丁包发给后台,最后把1.0.0的代码merge到trunk主线即可。
问题: 要给同一个版本多次打补丁包,又怎样弄呢?
直接在每次发布版本新建的分支上修复bug,然后每次打不同的补丁包,就需要叫后台返回不通的连接(为了区分该补丁包是否已加载过,上面后台接口的设计有讲到)。即都要以发布时的版本作为基础包进行bug修改。
问题:加载补丁包后,怎样才能让修改的bug生效呢?
解决:因为Tinker不是即时生效的。所以我们这里不用处理,加载完补丁包,用户退出下次进来就自然生效。
部分摘录至tinker issue。 需要hotfix aar(tinker的核心部分拉扯出来了)请私聊。后期会放入到自己github