公司项目的Bug、Crash统计一直用的的腾讯的 Bugly ,今日发现多了一个热更新(HotFix)功能,使用的技术是微信的方案 Tinker ,再三考虑后决定接入这个功能。javascript
Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码、So库以及资源,让应用可以在不须要从新安装的状况下实现更新。固然,你也可使用Tinker来更新你的插件。java
Tinker与其余热更新插件的比较:
android
其余关于Tinker的介绍请看Tinker的 wiki。git
咱们选择Tinker的道理很简单:稳定、开发透明。
而咱们选择经过Bugly来接入Tinker,由于它简单,不须要咱们再提供补丁下载接口和补丁管理后台。程序员
// tinker gradle插件
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.5')
// tinkersupport插件
classpath "com.tencent.bugly:tinker-support:latest.release"复制代码
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
ext {
// for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
tinkerEnabled = true
// for normal build
// old apk file to build patch apk
tinkerOldApkPath = "${bakPath}/app-release.apk"
// proguard mapping file to build patch apk
tinkerApplyMappingPath = "${bakPath}/mapping.txt"
// resource R.txt to build patch apk, must input if there is resource changed
tinkerApplyResourcePath = "${bakPath}/R.txt"
// only use for build all flavor, if not, just ignore this field
tinkerBuildFlavorDirectory = "${bakPath}/app-1124-23-03-52"
}
def getOldApkPath() {
return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath
}
def getApplyMappingPath() {
return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath
}
def getApplyResourceMappingPath() {
return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath
}
def getTinkerIdValue() {
return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()
}
def buildWithTinker() {
return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled
}
def getTinkerBuildFlavorDirectory() {
return ext.tinkerBuildFlavorDirectory
}
// 注意:必需要配置tinker-support
tinkerSupport {
enable = true
}
// 全局信息相关配置项
tinkerPatch {
oldApk = getOldApkPath() //打补丁包时必选, 基准包路径
ignoreWarning = false // 可选,默认false
useSign = true // 可选,默认true, 验证基准apk和patch签名是否一致
// 编译相关配置项
buildConfig {
applyMapping = getApplyMappingPath() // 可选,设置mapping文件,建议保持旧apk的proguard混淆方式
applyResourceMapping = getApplyResourceMappingPath() // 可选,设置R.txt文件,经过旧apk文件保持ResId的分配
tinkerId = "v1.0.0" // 必选,当前版本的惟一标识,能够是git版本号、versionName
}
// dex相关配置项
dex {
dexMode = "jar" // 可选,默认为jar
usePreGeneratedPatchDex = false // 可选,默认为false
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]
loader = ["com.tencent.tinker.loader.*", // 必选
"com.huidr.patient.HuiTinkerApplication",
]
}
// lib相关的配置项
lib {
pattern = ["lib/armeabi/*.so"]
}
// res相关的配置项
res {
pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = ["assets/sample_meta.txt"]
largeModSize = 100
}
// 用于生成补丁包中的'package_meta.txt'文件
packageConfig {
configField("patchMessage", "tinker is sample to use")
configField("platform", "all")
configField("patchVersion", "1.0")
}
// 7zip路径配置项,执行前提是useSign为true
sevenZip {
zipArtifact = "com.tencent.mm:SevenZip:1.1.10" // optional
}
}复制代码
咱们以前的自定义Application类如今是不能直接继承Application了,须要改为继承Tinker提供的DefaultApplicationLike类github
/** * Application代理类 */
public class ApplicationDelegate extends DefaultApplicationLike {
public ApplicationDelegate(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent, Resources[] resources, ClassLoader[] classLoader, AssetManager[] assetManager) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent, resources, classLoader, assetManager);
}
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.RELEASE) {
Bugly.init(getApplication(), BuildConfig.buglyAppId, false);
} else {
Bugly.init(getApplication(), BuildConfig.buglyAppId, true);
}
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
// you must install multiDex whatever tinker is installed!
MultiDex.install(base);
// 安装tinker
Beta.installTinker(this);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
getApplication().registerActivityLifecycleCallbacks(callbacks);
}
}复制代码
这个代理的Application类的时候方法和原来同样,只不过它的调用是由Tinker来调用了,而这个调用者就是咱们即将要定义的Application。微信
/** * TinkerApplication. * <p> * 注意:这个类集成TinkerApplication类,这里面不作任何操做,全部Application的代码都会放到ApplicationLike继承类当中<br/> * <pre> * 参数解析: * 参数1:int tinkerFlags 表示Tinker支持的类型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL * 参数2:String delegateClassName Application代理类 这里填写你自定义的ApplicationLike * 参数3:String loaderClassName Tinker的加载器,使用默认便可 * 参数4:boolean tinkerLoadVerifyFlag 加载dex或者lib是否验证md5,默认为false * </pre> */
public class HuiTinkerApplication extends TinkerApplication {
public HuiTinkerApplication() {
super(ShareConstants.TINKER_ENABLE_ALL, "com.huidr.patient.ApplicationDelegate",
"com.tencent.tinker.loader.TinkerLoader", false);
}
}复制代码
这里构造方法里的"com.huidr.patient.ApplicationDelegate"改为刚刚定义的Application代理类。
同时在AndroidMenifest.xml中把Application改为新定义的Appcation。
到这里,配置已经完成了。app
回到项目主module的build.gradle中,找到:ide
// dex相关配置项
dex {
dexMode = "jar" // 可选,默认为jar
usePreGeneratedPatchDex = false // 可选,默认为false
pattern = ["classes*.dex",
"assets/secondary-dex-?.jar"]
loader = ["com.tencent.tinker.loader.*", // 必选
"com.huidr.patient.HuiTinkerApplication",
]
}复制代码
将这里配置的Applicaiton改为你刚刚建立的。gradle
如今能够打包出ralease版本,获得app-release.apk文件,将其运行起来。
如今主要关注这部分配置就能够了
// 编译相关配置项
buildConfig {
applyMapping = getApplyMappingPath() // 可选,设置mapping文件,建议保持旧apk的proguard混淆方式
applyResourceMapping = getApplyResourceMappingPath() // 可选,设置R.txt文件,经过旧apk文件保持ResId的分配
tinkerId = "v1.0.0" // 必选,当前版本的惟一标识,能够是git版本号、versionName
}复制代码
最后修改tinkerId(当前版本的惟一标识,能够是git版本号、versionName)
运行后获得补丁app\build\outputs\patch\release\patch_signed_7zip.apk
这里最好把patch_signed_7zip.apk文件扩展名改为.zip,防止有些手机厂商对apk文件劫持。
打开Bugly热更新后台界面,选择发布补丁,并下发
如今将App退出从新打开2次,就能够看到更新后的结果了
第一次打开会监测补丁版本;
第二次打开会安装新补丁。
ps:若是发现补丁有问题,还可使用撤回功能,让App回复补丁前的样子。
感谢在一线上为热更新奋战的程序员,这是一条跪着走完的道路,正是有了他们背后的默默付出,咱们才能这么方便地用上热更新功能,致敬!
以上集成过程主要参考如下文档:
Bugly Android热更新使用指南
Tinker 接入指南