Android熱修復(fù)相關(guān)
Robust | 字節(jié)碼插樁 代理 自動(dòng)埋點(diǎn) |
---|---|
Tinker | dex差分(bsdiff差分不關(guān)心文件格式 二進(jìn)制全格式) 反射 類加載 |
Qzone | dex差分 反射 類加載 |
-
java中的類加載 雙親委托
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name);//判斷類是否已經(jīng)加載過(guò) if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false);//父類加載器優(yōu)先加載 } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name);//調(diào)用當(dāng)前類加載器的findClass方法進(jìn)行加載 // this is the defining class loader; record the stats } } return c; }
-
android中的類加載器
- BootClassLoader 系統(tǒng)類
- PathClassLoader
- DexClassLoader
PathClassLoader中反射獲取makePathElements()方法 得到補(bǔ)丁包Element[]
將新補(bǔ)丁包與原包合并 通過(guò)System.arraycopy(...)
CLASS_ISPREVERIFIED 標(biāo)記錯(cuò)誤 導(dǎo)致修復(fù)異常
使用javaessit進(jìn)行字節(jié)碼插樁 讓原來(lái)的所有class都引用補(bǔ)丁的類
img
gradle插樁
類比為 用 gson蔫饰,fastjson這類第三方框架來(lái)修改json文件肥哎。我們也可以利用 特定的手段來(lái)自由修改class文件厅各。這類技術(shù)框架有ASM济炎,AspectJ, Javassist等洛巢。 由于我們androidStudio用gradle來(lái)構(gòu)建項(xiàng)目扁眯,所以哆致,還需要我們自定義gradle插件涩维,來(lái)在合適的時(shí)機(jī) 使用ASM 這種技術(shù)框架來(lái)在class文件中修改字節(jié)碼內(nèi)容删掀。
javac命令之后翔冀,dx命令之前
gradle執(zhí)行項(xiàng)目構(gòu)建,是通過(guò)一個(gè)一個(gè)的task來(lái)進(jìn)行披泪。比如 將java文件用javac命令編譯為 class纤子,任務(wù)名字叫做::app:compileDebugJavaWithJavac
img
gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
@Override
void beforeEvaluate(Project project) {
println " add project evaluation lister beforeEvaluate,project path is: "+project
}
@Override
void afterEvaluate(Project project, ProjectState state) {
println " add project evaluation lister afterProject,project path is:"+project
}
}
在afterEvaluate解析完成之后執(zhí)行
anroid.getApplicationVariants().all{
variant ->
String variantName = variant.getName(); //debug 或者 release
String capitializeName = variantName.capitalize();//首字母大寫
Task dexTask = project.getTasks().findByName("transformClassWithDexBuilderFor" + capitializeName);
dexTask.doFrist{....打包任務(wù)之前進(jìn)行插樁}
}
ASM進(jìn)行插樁
ClassReader cr = new ClassReader(inputStream);
ClassWriter cw = new ClassWriter(cr,0);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5,cw){
public MethodVisitor visitMethod( final int access,final String name,final String desc,final String signature, final String[] exceptions) {
MethodVisitor mv = super.visiMethod(access,name,desc,signature,exceptions);
mv = new MethodVisitor(Opcodes.ASM4,mv){
visitlnsn(int opcode){
//在構(gòu)造方法中插入類的引用
if("<init>".equals(name)&&opcode==Opcodes.RETURN){
.....
}
super.visitlnsn(opcode);
}
}
return mv;
}
}
cr.accept(cv,0);//啟動(dòng)分析
Android N 混合編譯
AOT提前編譯 導(dǎo)致類被加載無(wú)法被替換
解決方式 使用運(yùn)行時(shí)替換PathClassloader
Thread.currentThread().setContextClassloader(classLoader);
LoaderApk+Resources+DrawableInflater(參考Tinker)