项目中我们总会遇到这样的问题刚刚发布版本就发现了一个严重错误,对用户的使用体验非常的差,所以需要立马更新. 但是如果全量更新的话,小则就是20M APK大小, 多则 50多 M. 这样频繁的让用户下载非常影响用户体验。 事实上我所在的项目组一直都是这么干的,个人感觉这样非常low。

Tinker的作用

Tinker就是为了解决这种问题而生的, 修改少量的代码,生成差分包,然后用户下载非常小的更新包,就可以解决问题。它是微信官方的Android热补丁解决方案,它支持动态下发代码、So库以及资源,让应用能够在不需要重新安装的情况下实现更新。当然,你也可以使用Tinker来更新你的插件。
https://github.com/Tencent/tinker
下面这个是 官方的demo代码, 下载源码 单独跑这一个demo就可以测试tinker
https://github.com/Tencent/tinker/tree/master/tinker-sample-android

如何接入

废话不多说 直接贴上我的demo代码。

引入依赖

E:\xxx\TinkerDemo\build.gradle

    dependencies {classpath 'com.android.tools.build:gradle:3.4.1'classpath ("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}")}

E:\xxx\TinkerDemo\app\build.gradle

apply plugin: 'com.android.application'
//apply tinker插件
apply plugin: 'com.tencent.tinker.patch'apply from: 'tinkerpatch.gradle' //引用tinkerpatch.gradle文件android {compileSdkVersion project.COMPILE_BUILD_SDK_VERSION as intdefaultConfig {applicationId "com.example.tinkerdemo"minSdkVersion project.MIN_SDK_VERSION as inttargetSdkVersion project.TARGET_SDK_VERSION as intmultiDexEnabled trueversionCode project.VERSION_CODE as intversionName project.VERSION_NAMEtestInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}signingConfigs {config {keyAlias 'key0'keyPassword '123456'storeFile file('../keystore.jks')storePassword '123456'}}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'signingConfig signingConfigs.config  //gradlew assembleRelease}}
}dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'com.android.support:appcompat-v7:28.0.0'implementation 'com.android.support.constraint:constraint-layout:1.1.3'testImplementation 'junit:junit:4.12'androidTestImplementation 'com.android.support.test:runner:1.0.2'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'//tinker核心sdk库     参与编译与打包api("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }annotationProcessor("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }compileOnly("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }api 'com.android.support:multidex:1.0.3'api 'pub.devrel:easypermissions:2.0.1'
}

配置一些Tinker的相关参数
E:\xxx\TinkerDemo\app\tinkerpatch.gradle


def bakPath = file("${buildDir}/bakApk/") //指定基准文件存放位置
ext {tinkerEnable = truetinkerOldApkPath = "${bakPath}/app-release-0718-15-46-33.apk"tinkerID = "1.0"//tinkerApplyMappingPath = "${bakPath}/"tinkerApplyResourcePath = "${bakPath}/app-release-0718-15-46-33-R.txt"
}def buildWithTinker() {return ext.tinkerEnable
}def getOldApkPath() {return ext.tinkerOldApkPath
}def getApplyMappingPath() {return ext.tinkerApplyMappingPath
}def getApplyResourceMappingPath() {return ext.tinkerApplyResourcePath
}def getTinkerIdValue() {return ext.tinkerID
}def getTinkerBuildFlavorDirectory(){return ext.tinkerBuildFlavorDirectory
}if (buildWithTinker()) {//启用tinkerapply plugin: 'com.tencent.tinker.patch'//所有tinker相关的参数配置tinkerPatch {oldApk = getOldApkPath() //指定old apk文件路径ignoreWarning = false   //不忽略tinker的警告,有警告则中止patch文件的生成useSign = true  //强制patch文件也使用签名tinkerEnable = buildWithTinker(); //指定是否启用tinkerbuildConfig {//applyMapping = getApplyMappingPath()  //指定old apk打包时所使用的混淆文件applyResourceMapping = getApplyResourceMappingPath()  //指定old apk的资源文件tinkerId = getTinkerIdValue() //指定TinkerIDkeepDexApply = false}dex {dexMode = "jar" //jar、rawpattern = ["classes*.dex", "assets/secondary-dex-?.jar"] //指定dex文件目录loader = ["androidjian.tinker.MyTinkerApplication"] //指定加载patch文件时用到的类}lib {pattern = ["libs/*/*.so"]}res {pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]//指定tinker可以修改的所有资源路径ignoreChange = ["assets/sample_meta.txt"] //指定不受影响的资源路径largeModSize = 100 //资源修改大小默认值}packageConfig {configField("patchMessage", "fix the 1.0 version's bugs")configField("patchVersion", "1.0")}}//判断当前是否配置多渠道List<String> flavors = new ArrayList<>();project.android.productFlavors.each { flavor ->flavors.add(flavor.name)}boolean hasFlavors = flavors.size() > 0/*** 复制基准包和其它必须文件到指定目录*/android.applicationVariants.all { variant ->/*** task type, you want to bak*/def taskName = variant.namedef date = new Date().format("MMdd-HH-mm-ss")tasks.all {if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {it.doLast {copy {def fileNamePrefix = "${project.name}-${variant.baseName}"def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPathif (variant.metaClass.hasProperty(variant, 'packageApplicationProvider')) {def packageAndroidArtifact = variant.packageApplicationProvider.get()if (packageAndroidArtifact != null) {from new File(packageAndroidArtifact.outputDirectory, variant.outputs.first().apkData.outputFileName)} else {from variant.outputs.first().mainOutputFile.outputFile}} else {from variant.outputs.first().outputFile}into destPathrename { String fileName ->fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")}from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"into destPathrename { String fileName ->fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")}from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"into destPathrename { String fileName ->fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")}}}}}}
}

测试TinkerDemo

E:\xxx\TinkerDemo\app\src\main\java\com\example\tinkerdemo\MainActivity.java

    public void startTinkerUpdate(View view) {File downloadCacheDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);String path  = downloadCacheDirectory.getAbsolutePath()+File.separator+"patch_signed.apk";Log.d(TAG, "startTinkerUpdate: "+path);TinkerManager.loadPatch(downloadCacheDirectory.getAbsolutePath()+File.separator+"patch_signed.apk");}

没有使用tinker之前的效果
接下来修改一下需要解决的问题, 这里我在布局文件里修改一些东西

对应的需要记住 这个版本的apk 以及 resource R文件,作为基准包。 如果你添加了混淆,需要添加对应的mapping文件。


得到差分包以后,就可以上传到服务器供用户下载,更新。 这里我就直接拷贝到手机的指定目录。

至此,我们的热更新就已经完成了。上面的图我们可以发现,差分包其实就只有几kb大小。 用户只需很短的时间就可以下载好更新包。

遇到的坑

  • 权限问题
    加载apk需要将apk复制到指定的文件目录进行资源 和dex文件的插入替换。需要SDK读写权限。 如果你是网络更新还需要网络权限。
  • android9.0问题
    用真机测试发现出现这样的bug
Tinker.DefaultLoadReporter: tinker load exception   ensureStringBlocks []

原因是ensureStringBlocks 已经被加入到黑名单,搜索 github tinker issues ,因为9.0原因,建议使用最新的tinker版本

  • 生成差分包处所
    https://github.com/Tencent/tinker/issues/961
    详细原因查看这个issues
Execution failed for task ':app:tinkerProcessReleaseResourceId'. > java.io.FileNotFoundException: build\intermediates\tinker_intermediates\values_backup

解决办法:
1.基准文件备份下
2.clean项目clean
3.打补丁包

github Demo地址

小试牛刀 --- Tinker集成教程相关推荐

  1. SpringMVC 3 Tiles 2.2.2集成教程

    Apache Tiles是基于Java的Web应用程序的流行且最常用的模板框架. 由于Struts 1.x使用Tiles作为其默认模板框架,因此Tiles变得更加流行. SpringMVC是一个MVC ...

  2. python 持续集成 教程_jenkins+python自动化测试持续集成教程

    一.首先我们安装Jenkins,我这里采用的是.msi应用程序,根据提示进行安装(傻瓜式),最后会打开默认的网页地址:http://localhost:8080 如果端口有冲突,可以去Jenkins的 ...

  3. MongoDB与Spring集成教程

    MongoDB与Spring集成教程 依赖POM 这里我们用到的是spring-data中一个集成mongodb的项目,首先在maven中添加对它的依赖,这里我用的是1.0.0.M5版本 <!- ...

  4. Spring JSF集成教程

    Spring JSF集成教程 欢迎来到Spring JSF集成教程.JSF是一个基于组件的框架,非常注重用户界面.而Spring框架核心原则是依赖注入.因此,将JSF与Spring框架集成是有意义的, ...

  5. iOS开发之第三方支付微信支付教程,史上最新最全第三方微信支付方式实现、微信集成教程,微信实现流程

    本章项目demo: https://github.com/zhonggaorong/weixinLoginDemo 本章不讲解: 微信sdk的集成 , 项目集成的文章请参照 (包含微信登录):   h ...

  6. 【环信IM集成教程】iOS端收到离线消息显示设置

    [集成教程]iOS端收到离线消息显示设置 查看更多官方集成教程:https://www.imgeek.org/video/https://www.imgeek.org/video/

  7. PHP在线支付接口集成教程

    网上支付(支付宝,财付通,易宝,银联)是电子支付的一种形式,它是通过第三方提供的与银行之间的支付接口进行的即时支付方式,这种方式的好处在于可以直接把资金从用户的银行卡中转账到网站账户中,汇款马上到账, ...

  8. NearbyService超简单集成教程get!助你打造一款更加便捷的社交聊天类应用

    背景 "附近的人"."面对面建群"."近距离点对点本地聊天"-这几个常见的功能都会出现在各大社交类软件中.可通常情况下,"附近的人 ...

  9. eai app_EAI的Spring集成教程

    eai app 课程大纲 Spring Integration是用于企业应用程序集成的开源框架. 这是一个轻量级的框架,建立在核心Spring框架之上. 它旨在支持开发事件驱动的体系结构和以消息为中心 ...

最新文章

  1. bff v2ex_语音备忘录的BFF-如何通过Machine Learning简化Speech2Text
  2. Vivado IP核生成设置
  3. HGST:中国将成为新云端运算大国
  4. 计算机视觉开源库OpenCV绘制轮廓,并将轮廓排序~
  5. CodeForces - 566A Matching Names(字典树上贪心)
  6. eclipse oracle驱动位置,【求助】eclipse导入了Oracle的驱动包连不上Oracle
  7. 最简单的EasyUI菜单栏
  8. 优化器,sgd,adam等
  9. RTCM3消息类型介绍
  10. c语言摇奖小程序,小程序实现抽奖动画
  11. 火星坐标系解密-iDesktopX空间数据处理
  12. 坦克世界怎么显示服务器准心,坦克世界8.0环境设置详细教程
  13. 数据库系统的基本组成
  14. 那些年,Java程序员用过的开发工具
  15. 在国内如何使用gmail_如何在Gmail中使用自定义背景
  16. 关于PS课程中字体部分的学习总结
  17. 传统的招投标or在线招投标
  18. python3--日期时间处理最近n个自然周计算
  19. IMWeb提升营Day5
  20. 使用select2实现多功能下拉框,select2中文api

热门文章

  1. (转)导师是会撒谎的扳手(蒋方舟)
  2. Laya贝塞尔运动(金币获取运动)
  3. IIS 绑定多个证书错误解决方法
  4. 电路仿真软件详谈(24),基于proteus电路仿真软件的电压表印刷电路板设计
  5. 计算机有usb无法识别怎么办,电脑识别不到usb设备怎么办
  6. Linux系统操作(4):Linux Ubuntu 屏幕清理命令
  7. java在字符串前后加引号,Java String字符串内容实现添加双引号
  8. css中width、height、margin、padding默认值总结
  9. 物联网卡管理平台有什么用
  10. 解决网页不允许复制粘贴