本文为大型面试分析,需要的可以点个关注
废话不多说,先来一张此系列文的面试资料目录
1.热修复是什么
热修复无疑是这2年较火的新技术,是作为安卓工程师必学的技能之一。在热修复出现之前,一个已经上线的app中如果出现了bug,即使是一个非常小的bug,不及时更新的话有可能存在风险,若要及时更新就得将app重新打包发布到应用市场后,让用户再一次下载,这样就大大降低了用户体验,当热修复出现之后,这样的问题就不再是问题了。
目前较火的热修复方案大致分为两派,分别是:
1.阿里系:spohix、andfix:从底层二进制入手(c语言)。
2.腾讯系:tinker:从java加载机制入手。
2.有接触过tinker吗
答: 有接触过Tinker的 Tinker是一个比较优异修复架构
3.修复的原理是什么
答: 关于bug的概念自己百度百科吧,我认为的bug一般有2种(可能不太准确):
代码功能不符合项目预期,即代码逻辑有问题。
程序代码不够健壮导致App运行时崩溃。
这两种情况一般是一个或多个class出现了问题,在一个理想的状态下,我们只需将修复好的这些个class更新到用户手机上的app中就可以修复这些bug了。但说着简单,要怎么才能动态更新这些class呢?其实,不管是哪种热修复方案,肯定是如下几个步骤:
下发补丁(内含修复好的class)到用户手机,即让app从服务器上下载(网络传输)
app通过**“某种方式”,使补丁中的class被app调用(本地更新)
这里的"某种方式"**,对本篇而言,就是使用Android的类加载器,通过类加载器加载这些修复好的class,覆盖对应有问题的class,理论上就能修复bug了。所以,下面就先来了解和分析Android中的类加载器吧。
4. Tinker源码分析
Tinker工程结构
直接从github上clone Tinker的源码进行食用如下:
接入流程
1.gradle相关配置主项目中build.gradle加入
buildscript {
dependencies {
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.8.1')
}
}
在app工程中build.gradle加入
dependencies {
//可选,用于生成application类
provided('com.tencent.tinker:tinker-android-anno:1.8.1')
//tinker的核心库
compile('com.tencent.tinker:tinker-android-lib:1.8.1')
}
...
...
//apply tinker插件
apply plugin: 'com.tencent.tinker.patch'
这里需要注意tinker编译阶段会判断一个TinkerId的字段,该字段默认由git提交记录生成HEAD(git rev-parse --short HEAD)而且是在rootproject中执行的git命令,所以个别工程可能在rootproject目录没有git init过,可以选择在那初始化git或者自定义gradle修改gitSha方法。
出包还是使用正常的build过程,测试阶段选择assembleDebug,Tinker产出patch使用gradle tinkerPatchDebug同样也支持Flavor和Variant,Tiner会在主工程build目录下创建bakApk,下面会有一个app-yydd-hh-mm-ss的目录里面对应有Favor子目录里面包含了通过assemble出的apk包。在build目录下的outputs中有tinkerPatch里面同样也区分了build variant产物。
需要注意的是在debug出包测试过程中需要修改gradle的参数
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-debug-1018-17-58-54.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"
//使用buildvariants修改此处app信息作为基准包
tinkerBuildFlavorDirectory = "${bakPath}/app-1020-11-52-37"
}
而release出包可以直接在gradle命令带上后缀-POLD_APK= -PAPPLY_MAPPING= -PAPPLY_RESOURCE=
1.Application改造
Tinker采用了代码框架的方案来解决应用启动加载默认Application导致patch无法修复它。原理就是使用一个ApplicationLike代理类来完成原Application的功能,把所有原理Application中的代码逻辑移动到ApplicationLike中,然后删除原来的Application类通过注解让Tinker自动生成默认Application。
@DefaultLifeCycle(application = "com.*.Application",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class ApplicationLike extends DefaultApplicationLike {
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
//you must install multiDex whatever tinker is installed!
MultiDex.install(base);
TinkerManager.setTinkerApplicationLike(this);
TinkerManager.initFastCrashProtect();
//should set before tinker is installed
TinkerManager.setUpgradeRetryEnable(true);
//installTinker after load multiDex
//or you can put com.tencent.tinker.** to main dex
TinkerManager.installTinker(this);
}
}
TinkerManager.java
public static void installTinker(ApplicationLike appLike) {
if (isInstalled) {
TinkerLog.w(TAG, "install tinker, but has installed, ignore");
return;
}
//or you can just use DefaultLoadReporter
LoadReporter loadReporter = new TinkerLoadReporter(appLike.getApplication());
//or you can just use DefaultPatchReporter
PatchReporter patchReporter = new TinkerPatchReporter(appLike.getApplication());
//or you can just use DefaultPatchListener
PatchListener patchListener = new TinkerPatchListener(appLike.getApplication());
//you can set your own upgrade patch if you need
AbstractPatch upgradePatchProcessor = new UpgradePatch();
TinkerInstaller.install(appLike,
loadReporter, patchReporter, patchListener,
TinkerResultService.class, upgradePatchProcessor);