微信热补丁修复框架--tinker

来源:互联网 发布:国家网络应急中心笔试 编辑:程序博客网 时间:2024/06/07 10:00
1.资源:
  • github地址:https://github.com/Tencent/tinker
  • wiki地址(使用文档):https://github.com/Tencent/tinker/wiki
  • 补丁管理平台tinkerpatch(官方推荐):
            平台地址:http://tinkerpatch.com/     
            github:https://github.com/simpleton/tinker_server_client
  • 补丁管理平台bugly(腾讯):https://bugly.qq.com/v2/

2.局限:
由于原理与系统限制,Tinker有以下已知问题:

  • Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件;
  • 由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码;
  • 在Android N上,补丁对应用启动时间有轻微的影响;
  • 不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed";
  • tinker的一般模式并不支持加固,需要使用usePreGeneratedPatchDex模式,即提前生成补丁模式。某些加固工具可能会将非exported的四大组件类名替换,这些类将无法修改。对于Android N之后的设备,本模式可能会因为内联而出现问题,建议过滤N之后的设备;
  • 对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标。
  • 如果手机版本为api23,gradle中配置的targetsdk版本也是23,补丁打不上(我的华为p9手机和genymotion模拟器是这样的)
  • 补丁需要重启才能生效

3.官方demo
从github上下载源码+demo:https://github.com/Tencent/tinker
在android studio 中点击 tinker-sample-android 项目导入就好了(不要导入别的,导入根目录会识别不了app module)

首先一点,在app的build.gradle文件中找到tinkerId = getTinkerIdValue()并将其替换成tinkerId = "tinkerId",其中后面的值可以随意设置,比如可以设置为版本号或版本名,这个用于补丁校验的,如果补丁id和基准apk的tinkerid不一致,就打不上去
 再替换ignoreWarning = falseignoreWarning = true.

集成tinker的app在as中直接运行时不支持instant run,请先关闭,或者不直接点击run或debug,使用gradle/gradlew运行相关的assemble任务,生成apk再安装到手机

编译运行原版apk

使用assembleDebug 任务(如下图)编译并生成debug apk,如果是release,就运行assembleRelease



这里注意 android studio run 按钮生成的apk可能不会加入签名,使用tinker插件生成补丁包时候回导致签名不一致问题,建议使用assembleXXX 任务生成基准包,而且直接run 还会导致:

对照基准apk包和补丁apk发现补丁apk比基准apk大了1M多,查看log.text发现居然多了很多莫名其妙的图片,详见wiki issue:https://github.com/Tencent/tinker/issues/103 

各种配置详见官方wiki:

https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97


此时Tinker会在工程的app/build/bakApk/目录下保存这个时间点打包好的apk文件(这里就是基准包---所谓基准包 就是未启用热修复的包),找到刚才生成的apk文件(记住,一定要配置对基准包,基准包就是现在在运行,将来要打补丁的app对应的当时生成的apk),复制其完整文件名,在app的build.gradle文件找到tinkerOldApkPath这一项设置,并将其设置为tinkerOldApkPath = "${bakPath}/<刚才生成的apk文件名>"(见图2)


图2

在build.gradle文件找到tinkerApplyResourcePath这一项设置,并将其设置为tinkerApplyResourcePath = "${bakPath}/<刚才生成的apk文件名下面的txt文件>"
apk,R.txt,mapping.txt等其他几个backApk目录下面生成的东西和gradle中ext各项对应
形如

     tinkerOldApkPath = "${bakPath}/app-debug-1018-17-32-47.apk"    //proguard mapping file to build patch apk    tinkerApplyMappingPath = "${bakPath}/app-debug-1018-17-32-47-mapping.txt"    //resource R.txt to build patch apk, must input if there is resource changed    tinkerApplyResourcePath = "${bakPath}/app-debug-1018-17-32-47-R.txt"    //only use for build all flavor, if not, just ignore this field    tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"

注意如果只改了代码,没有涉及资源的修改,也没有做混淆,也没有多渠道,后面也配置了oldapk属性,这部分可以不配置

修改源码 生成新版apk 补丁


修改源码,运行gradlew tinkerPatchDebug(或release)

运行完毕后,补丁apk文件所在位置见图5




将patch_signed_7zip.apk这个文件拷贝到Android设备的ExternalStorageDirectory()路径下(即SD卡目录下).文件的路径和文件名可以随意设定,只需在MainActivity中指明补丁Apk路径即可(见图6),正式使用时,上传补丁到管理后台,后缀最好不要apk,以免被运营商挟持

安装热修复补丁 观察程序变化

点击APP主界面中的LOAD PATCH加载补丁,提示成功后,点击KILL SELF结束当前进程(Tinker必须重启才能热修复成功),重新启动后,即可发现变化.


常见问题:

网络情况不佳时,tinker插件所依赖的库或者插件缺失(如 com.tencent.mm:SevenZip 缺失)

1.1 原因:下载地址是https开头,导致下载不下来
1.2 解决办法:找到C盘用户目录下.gradle目录下的cache目录(形如 C:\Users\hasee.gradle\caches)
删除该文件夹下所有文件以及文件夹然后将 根目录(Project:)build.gradle中把



jcenter()
更改为
jcenter(){ url 'http://jcenter.bintray.com/' }
再重新编译。(相当于再次重新下载依赖)

Error:Execution failed for task ':app:transformClassesWithJarMergingForDebug'. BSDiff$1.class

解决办法: clean project

Error:(9, 0) Your project path contains non-ASCII characters编译失败

原因:工程目录处于中文路径下
解决办法:不处于中文路径下就好了

加载补丁时补丁加载失败

补丁加载失败的原因很多,具体原因可以在logcat中查看,但是需要确保logcat的设置如下,否则tinker合成补丁日志输出获取不到



常见加载失败有签名校验的问题



解决办法:注意不要搞混 tinkerPatchDebug gradle任务 和 tinkerPatchReleas gradle任务 对应生成的
patch_signed_7zip,debug签名就用 tinkerPatchDebug gradle任务 生成的 patch_signed_7zip



4.结合补丁管理后台实际使用:
使用bugly时注意:不要选择tinkerPatch目录下的补丁包,不然上传会有问题
两个管理平台,都注意以下:

1.多个补丁,只会更新最新上传的那个补丁(自动生成的那个序号最大的)

2.只会更新对应版本配置下的补丁

3.如果当前版本为base,相对base生成了第一个补丁patch1,相对base生成了第二个补丁patch2,手机上安装的base版本可以更新patch1,也可以直接更新patch2

如果patch2是相对base+patch1代码生成的apk生成的补丁,当前安装的是base,无法更新,当前即使先更新到patch1,也没法更新patch2补丁

当前原始安装的app必须作为基准去生成patch,才能在当前原始安装的app上更新这个patch

4.如果patch1增加一个功能1app成功打上patch1patch2中删除了patch1的功能1,增加了功能2app成功打上patch2,结果功能1消失,功能2出来,app应该只加载最新的一个patch,并不是多个patch功能叠加

5.如果服务器有多个补丁都没有更新,只会更新最新的补丁(补丁版本号最大的),如果当前的补丁版本号比服务器上最大补丁版本号的大或者相等,不会更新补丁






0 0