一喘沿、概述
最新github上開源了很多熱補(bǔ)丁動態(tài)修復(fù)框架斤讥,大致有:
https://github.com/dodola/HotFix
https://github.com/jasonross/Nuwa
https://github.com/bunnyblue/DroidFix
上述三個(gè)框架呢碟渺,根據(jù)其描述奄侠,原理都來自:安卓App熱補(bǔ)丁動態(tài)修復(fù)技術(shù)介紹嫩絮,以及Android dex分包方案泻骤,所以這倆篇?jiǎng)?wù)必要看。這里就不對三個(gè)框架做過多對比了戏羽,因?yàn)樵矶家恢碌I瘢瑢?shí)現(xiàn)的代碼可能差異并不是特別大。
有興趣的直接看這篇原理文章始花,加上上面框架的源碼基本就可以看懂了妄讯。當(dāng)然了,本篇博文也會做個(gè)上述框架源碼的解析酷宵,以及在整個(gè)實(shí)現(xiàn)過程中用到的技術(shù)的解析亥贸。
對于熱修復(fù)的原理浇垦,如果你看了上面的兩篇文章炕置,相信你已經(jīng)大概明白了。重點(diǎn)需要知道的就是讹俊,Android的ClassLoader體系,android中加載類一般使用的是PathClassLoader和DexClassLoader煌抒,首先看下這兩個(gè)類的區(qū)別:
對于PathClassLoader仍劈,從文檔上的注釋來看:
Provides a simple {@link ClassLoader} implementation that operates
on a list of files and directories in the local file system, but
does not attempt to load classes from the network. Android uses
this class for its system class loader and for its application
class loader(s).
可以看出,Android是使用這個(gè)類作為其系統(tǒng)類和應(yīng)用類的加載器寡壮。并且對于這個(gè)類呢贩疙,只能去加載已經(jīng)安裝到Android系統(tǒng)中的apk文件。
對于DexClassLoader况既,依然看下注釋:
A class loader that loads classes from {@code .jar} and
{@code .apk} files containing a {@code classes.dex} entry.
This can be used to execute code not installed as part of an application.
可以看出这溅,該類呢,可以用來從.jar和.apk類型的文件內(nèi)部加載classes.dex文件棒仍”ィ可以用來執(zhí)行非安裝的程序代碼。
ok莫其,如果大家對于插件化有所了解癞尚,肯定對這個(gè)類不陌生耸三,插件化一般就是提供一個(gè)apk(插件)文件,然后在程序中l(wèi)oad該apk浇揩,那么如何加載apk中的類呢仪壮?其實(shí)就是通過這個(gè)DexClassLoader,具體的代碼我們后面有描述胳徽。
ok积锅,到這里,大家只需要明白养盗,Android使用PathClassLoader作為其類加載器缚陷,DexClassLoader可以從.jar和.apk類型的文件內(nèi)部加載classes.dex文件就好了。
上面我們已經(jīng)說了往核,Android使用PathClassLoader作為其類加載器箫爷,那么熱修復(fù)的原理具體是?
ok铆铆,對于加載類,無非是給個(gè)classname丹喻,然后去findClass薄货,我們看下源碼就明白了。
PathClassLoader和DexClassLoader都繼承自BaseDexClassLoader碍论。在BaseDexClassLoader中有如下源碼:
#BaseDexClassLoader@OverrideprotectedClassfindClass(String name)throwsClassNotFoundException {? ? Class clazz = pathList.findClass(name);if(clazz ==null) {thrownewClassNotFoundException(name);? ? }returnclazz;}#DexPathListpublicClassfindClass(String name) {for(Element element : dexElements) {? ? ? ? DexFile dex = element.dexFile;if(dex !=null) {? ? ? ? ? ? Class clazz = dex.loadClassBinaryName(name, definingContext);if(clazz !=null) {returnclazz;? ? ? ? ? ? }? ? ? ? }? ? }returnnull;}#DexFilepublicClassloadClassBinaryName(String name, ClassLoader loader) {returndefineClass(name, loader, mCookie);}privatenativestaticClassdefineClass(String name, ClassLoader loader,intcookie);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
可以看出呢谅猾,BaseDexClassLoader中有個(gè)pathList對象,pathList中包含一個(gè)DexFile的集合dexElements鳍悠,而對于類加載呢税娜,就是遍歷這個(gè)集合,通過DexFile去尋找藏研。
ok敬矩,通俗點(diǎn)說:
一個(gè)ClassLoader可以包含多個(gè)dex文件,每個(gè)dex文件是一個(gè)Element蠢挡,多個(gè)dex文件排列成一個(gè)有序的數(shù)組dexElements弧岳,當(dāng)找類的時(shí)候,會按順序遍歷dex文件业踏,然后從當(dāng)前遍歷的dex文件中找類禽炬,如果找類則返回,如果找不到從下一個(gè)dex文件繼續(xù)查找勤家。(來自:安卓App熱補(bǔ)丁動態(tài)修復(fù)技術(shù)介紹)
那么這樣的話腹尖,我們可以在這個(gè)dexElements中去做一些事情,比如伐脖,在這個(gè)數(shù)組的第一個(gè)元素放置我們的patch.jar热幔,里面包含修復(fù)過的類乐设,這樣的話,當(dāng)遍歷findClass的時(shí)候断凶,我們修復(fù)的類就會被查找到伤提,從而替代有bug的類。
說到這认烁,你可能已經(jīng)露出笑容了肿男,原來熱修復(fù)原理這么簡單。不過却嗡,還存在一個(gè)CLASS_ISPREVERIFIED的問題舶沛,對于這個(gè)問題呢,詳見:安卓App熱補(bǔ)丁動態(tài)修復(fù)技術(shù)介紹該文有圖文詳解窗价。
ok如庭,對于CLASS_ISPREVERIFIED,還是帶大家理一下:
根據(jù)上面的文章撼港,在虛擬機(jī)啟動的時(shí)候坪它,當(dāng)verify選項(xiàng)被打開的時(shí)候,如果static方法帝牡、private方法往毡、構(gòu)造函數(shù)等,其中的直接引用(第一層關(guān)系)到的類都在同一個(gè)dex文件中靶溜,那么該類就會被打上CLASS_ISPREVERIFIED標(biāo)志开瞭。
那么,我們要做的就是罩息,阻止該類打上CLASS_ISPREVERIFIED的標(biāo)志嗤详。
注意下,是阻止引用者的類瓷炮,也就是說葱色,假設(shè)你的app里面有個(gè)類叫做LoadBugClass,再其內(nèi)部引用了BugClass娘香。發(fā)布過程中發(fā)現(xiàn)BugClass有編寫錯(cuò)誤冬筒,那么想要發(fā)布一個(gè)新的BugClass類,那么你就要阻止LoadBugClass這個(gè)類打上CLASS_ISPREVERIFIED的標(biāo)志茅主。
也就是說舞痰,你在生成apk之前,就需要阻止相關(guān)類打上CLASS_ISPREVERIFIED的標(biāo)志了诀姚。對于如何阻止响牛,上面的文章說的很清楚,讓LoadBugClass在構(gòu)造方法中,去引用別的dex文件呀打,比如:hack.dex中的某個(gè)類即可矢赁。
ok,總結(jié)下:
其實(shí)就是兩件事:1贬丛、動態(tài)改變BaseDexClassLoader對象間接引用的dexElements撩银;2、在app打包的時(shí)候豺憔,阻止相關(guān)類去打上CLASS_ISPREVERIFIED標(biāo)志额获。
如果你沒有看明白,沒事恭应,多看幾遍抄邀,下面也會通過代碼來說明。
三昼榛、阻止相關(guān)類打上CLASS_ISPREVERIFIED標(biāo)志
ok境肾,接下來的代碼基本上會通過https://github.com/dodola/HotFix所提供的代碼來講解。
那么胆屿,這里拿具體的類來說:
大致的流程是:在dx工具執(zhí)行之前奥喻,將LoadBugClass.class文件呢,進(jìn)行修改非迹,再其構(gòu)造中添加System.out.println(dodola.hackdex.AntilazyLoad.class)环鲤,然后繼續(xù)打包的流程。注意:AntilazyLoad.class這個(gè)類是獨(dú)立在hack.dex中彻秆。
ok楔绞,這里大家可能會有2個(gè)疑問:
如何去修改一個(gè)類的class文件
如何在dx之前去進(jìn)行疑問1的操作
這里我們使用javassist來操作结闸,很簡單:
ok,首先我們新建幾個(gè)類:
packagedodola.hackdex;publicclassAntilazyLoad{}packagedodola.hotfix;publicclassBugClass{publicStringbug()? ? {return"bug class";? ? }}packagedodola.hotfix;publicclassLoadBugClass{publicStringgetBugString()? ? {? ? ? ? BugClass bugClass =newBugClass();returnbugClass.bug();? ? }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
注意下,這里的package五垮,我們要做的是抠忘,上述類正常編譯以后產(chǎn)生class文件。比如:LoadBugClass.class结耀,我們在LoadBugClass.class的構(gòu)造中去添加一行:
System.out.println(dodola.hackdex.AntilazyLoad.class)
1
下面看下操作類:
packagetest;importjavassist.ClassPool;importjavassist.CtClass;importjavassist.CtConstructor;publicclassInjectHack{publicstaticvoidmain(String[] args)? ? {try{? ? ? ? ? ? String path ="/Users/zhy/develop_work/eclipse_android/imooc/JavassistTest/";? ? ? ? ? ? ClassPool classes = ClassPool.getDefault();? ? ? ? ? ? classes.appendClassPath(path +"bin");//項(xiàng)目的bin目錄即可CtClass c = classes.get("dodola.hotfix.LoadBugClass");? ? ? ? ? ? CtConstructor ctConstructor = c.getConstructors()[0];? ? ? ? ? ? ctConstructor? ? ? ? ? ? ? ? ? ? .insertAfter("System.out.println(dodola.hackdex.AntilazyLoad.class);");? ? ? ? ? ? c.writeFile(path +"/output");? ? ? ? }catch(Exception e)? ? ? ? {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ok留夜,點(diǎn)擊run即可了,注意項(xiàng)目中導(dǎo)入javassist-*.jar的包图甜。
首先拿到ClassPool對象碍粥,然后添加classpath,如果你有多個(gè)classpath可以多次調(diào)用黑毅。然后從classpath中找到LoadBugClass嚼摩,拿到其構(gòu)造方法,在其最后插入一行代碼。ok枕面,代碼很好懂愿卒。
ok,我們反編譯看下我們生成的class文件:
ok潮秘,關(guān)于javassist琼开,如果有興趣的話,大家可以參考幾篇文章學(xué)習(xí)下:
http://www.ibm.com/developerworks/cn/java/j-dyn0916/
http://zhxing.iteye.com/blog/1703305
ok枕荞,這個(gè)就結(jié)合https://github.com/dodola/HotFix的源碼來說了柜候。
將其源碼導(dǎo)入之后,打開app/build.gradle
apply plugin: 'com.android.application'task('processWithJavassist')<< {StringclassPath=file('build/intermediates/classes/debug')//項(xiàng)目編譯class所在目錄dodola.patch.PatchClass.process(classPath,project(':hackdex').buildDir.absolutePath+ '/intermediates/classes/debug')//第二個(gè)參數(shù)是hackdex的class所在目錄}android{applicationVariants.all{variant->variant.dex.dependsOn<
1
2
3
4
5
6
7
8
9
10
11
12
13
你會發(fā)現(xiàn)买猖,在執(zhí)行dx之前改橘,會先執(zhí)行processWithJavassist這個(gè)任務(wù)。這個(gè)任務(wù)的作用呢玉控,就和我們上面的代碼一致了飞主。而且源碼也給出了,大家自己看下高诺。
ok碌识,到這呢,你就可以點(diǎn)擊run了虱而。ok筏餐,有興趣的話,你可以反編譯去看看dodola.hotfix.LoadBugClass這個(gè)類的構(gòu)造方法中是否已經(jīng)添加了改行代碼牡拇。
關(guān)于反編譯的用法魁瞪,工具等,參考:http://blog.csdn.net/lmj623565791/article/details/23564065
ok惠呼,到此我們已經(jīng)能夠正常的安裝apk并且運(yùn)行了导俘。但是目前還未涉及到打補(bǔ)丁的相關(guān)代碼。
四剔蹋、動態(tài)改變BaseDexClassLoader對象間接引用的dexElements
ok旅薄,這里就比較簡單了,動態(tài)改變一個(gè)對象的某個(gè)引用我們反射就可以完成了泣崩。
不過這里需要注意的是少梁,還記得我們之前說的,尋找class是遍歷dexElements矫付;然后我們的AntilazyLoad.class實(shí)際上并不包含在apk的classes.dex中凯沪,并且根據(jù)上面描述的需要,我們需要將AntilazyLoad.class這個(gè)類打成獨(dú)立的hack_dex.jar买优,注意不是普通的jar妨马,必須經(jīng)過dx工具進(jìn)行轉(zhuǎn)化樟遣。
具體做法:
jar cvf hack.jar dodola/hackdex/*
dx? --dex --output hack_dex.jar hack.jar
1
2
如果,你沒有辦法把那一個(gè)class文件搞成jar身笤,去百度一下…
ok豹悬,現(xiàn)在有了hack_dex.jar,這個(gè)是干嘛的呢液荸?
應(yīng)該還記得瞻佛,我們的app中部門類引用了AntilazyLoad.class,那么我們必須在應(yīng)用啟動的時(shí)候娇钱,降這個(gè)hack_dex.jar插入到dexElements伤柄,否則肯定會出事故的。
那么文搂,Application的onCreate方法里面就很適合做這件事情适刀,我們把hack_dex.jar放到assets目錄。
下面看hotfix的源碼:
/*
* Copyright (C) 2015 Baidu, Inc. All Rights Reserved.
*/packagedodola.hotfix;importandroid.app.Application;importandroid.content.Context;importjava.io.File;importdodola.hotfixlib.HotFix;/**
* Created by sunpengfei on 15/11/4.
*/publicclassHotfixApplicationextendsApplication{@OverridepublicvoidonCreate()? ? {super.onCreate();? ? ? ? File dexPath =newFile(getDir("dex", Context.MODE_PRIVATE),"hackdex_dex.jar");? ? ? ? Utils.prepareDex(this.getApplicationContext(), dexPath,"hackdex_dex.jar");? ? ? ? HotFix.patch(this, dexPath.getAbsolutePath(),"dodola.hackdex.AntilazyLoad");try{this.getClassLoader().loadClass("dodola.hackdex.AntilazyLoad");? ? ? ? }catch(ClassNotFoundException e)? ? ? ? {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
ok煤蹭,在app的私有目錄創(chuàng)建一個(gè)文件笔喉,然后調(diào)用Utils.prepareDex將assets中的hackdex_dex.jar寫入該文件。
接下來HotFix.patch就是去反射去修改dexElements了硝皂。我們深入看下源碼:
/*
* Copyright (C) 2015 Baidu, Inc. All Rights Reserved.
*/packagedodola.hotfix;/**
* Created by sunpengfei on 15/11/4.
*/publicclassUtils{privatestaticfinalintBUF_SIZE =2048;publicstaticbooleanprepareDex(Context context, File dexInternalStoragePath, String dex_file) {? ? ? ? BufferedInputStream bis =null;? ? ? ? OutputStream dexWriter =null;? ? ? ? bis =newBufferedInputStream(context.getAssets().open(dex_file));? ? ? ? dexWriter =newBufferedOutputStream(newFileOutputStream(dexInternalStoragePath));byte[] buf =newbyte[BUF_SIZE];intlen;while((len = bis.read(buf,0, BUF_SIZE)) >0) {? ? ? ? ? ? dexWriter.write(buf,0, len);? ? ? ? }? ? ? ? dexWriter.close();? ? ? ? bis.close();returntrue;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ok常挚,其實(shí)就是文件的一個(gè)讀寫,將assets目錄的文件稽物,寫到app的私有目錄中的文件奄毡。
下面主要看patch方法
/*
* Copyright (C) 2015 Baidu, Inc. All Rights Reserved.
*/packagedodola.hotfixlib;importandroid.annotation.TargetApi;importandroid.content.Context;importjava.io.File;importjava.lang.reflect.Array;importjava.lang.reflect.Field;importjava.lang.reflect.InvocationTargetException;importdalvik.system.DexClassLoader;importdalvik.system.PathClassLoader;/* compiled from: ProGuard */publicfinalclassHotFix{publicstaticvoidpatch(Context context, String patchDexFile, String patchClassName)? ? {if(patchDexFile !=null&&newFile(patchDexFile).exists())? ? ? ? {try{if(hasLexClassLoader())? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? injectInAliyunOs(context, patchDexFile, patchClassName);? ? ? ? ? ? ? ? }elseif(hasDexClassLoader())? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? injectAboveEqualApiLevel14(context, patchDexFile, patchClassName);? ? ? ? ? ? ? ? }else{? ? ? ? ? ? ? ? ? ? injectBelowApiLevel14(context, patchDexFile, patchClassName);? ? ? ? ? ? ? ? }? ? ? ? ? ? }catch(Throwable th)? ? ? ? ? ? {? ? ? ? ? ? }? ? ? ? }? ? } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
這里很據(jù)系統(tǒng)中ClassLoader的類型做了下判斷,原理都是反射贝或,我們看其中一個(gè)分支hasDexClassLoader();
privatestaticbooleanhasDexClassLoader(){try{? ? ? ? Class.forName("dalvik.system.BaseDexClassLoader");returntrue;? ? }catch(ClassNotFoundException e)? ? {returnfalse;? ? }}privatestaticvoidinjectAboveEqualApiLevel14(Context context, String str, String str2)throwsClassNotFoundException, NoSuchFieldException, IllegalAccessException{? ? PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();? ? Object a = combineArray(getDexElements(getPathList(pathClassLoader)),? ? ? ? ? ? getDexElements(getPathList(newDexClassLoader(str, context.getDir("dex",0).getAbsolutePath(), str, context.getClassLoader()))));? ? Object a2 = getPathList(pathClassLoader);? ? setField(a2, a2.getClass(),"dexElements", a);? ? pathClassLoader.loadClass(str2);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
首先查找類dalvik.system.BaseDexClassLoader吼过,如果找到則進(jìn)入if體。
在injectAboveEqualApiLevel14中咪奖,根據(jù)context拿到PathClassLoader盗忱,然后通過getPathList(pathClassLoader),拿到PathClassLoader中的pathList對象赡艰,在調(diào)用getDexElements通過pathList取到dexElements對象售淡。
ok斤葱,那么我們的hack_dex.jar如何轉(zhuǎn)化為dexElements對象呢慷垮?
通過源碼可以看出,首先初始化了一個(gè)DexClassLoader對象揍堕,前面我們說過DexClassLoader的父類也是BaseDexClassLoader料身,那么我們可以通過和PathClassLoader同樣的方式取得dexElements。
ok衩茸,到這里芹血,我們?nèi)〉昧耍到y(tǒng)中PathClassLoader對象的間接引用dexElements,以及我們的hack_dex.jar中的dexElements幔烛,接下來就是合并這兩個(gè)數(shù)組了啃擦。
可以看到上面的代碼使用的是combineArray方法。
合并完成后饿悬,將新的數(shù)組通過反射的方式設(shè)置給pathList.
接下來看一下反射的細(xì)節(jié):
privatestaticObjectgetPathList(Object obj)throwsClassNotFoundException, NoSuchFieldException,? ? ? ? ? ? IllegalAccessException{returngetField(obj, Class.forName("dalvik.system.BaseDexClassLoader"),"pathList");}privatestaticObjectgetDexElements(Object obj)throwsNoSuchFieldException, IllegalAccessException{returngetField(obj, obj.getClass(),"dexElements");}privatestaticObjectgetField(Object obj, Class cls, String str)throwsNoSuchFieldException, IllegalAccessException{? ? Field declaredField = cls.getDeclaredField(str);? ? declaredField.setAccessible(true);returndeclaredField.get(obj);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
其實(shí)都是取成員變量的過程令蛉,應(yīng)該很容易懂~~
privatestaticObjectcombineArray(Object obj, Object obj2){? ? Class componentType = obj2.getClass().getComponentType();intlength = Array.getLength(obj2);intlength2 = Array.getLength(obj) + length;? ? Object newInstance = Array.newInstance(componentType, length2);for(inti =0; i < length2; i++)? ? {if(i < length)? ? ? ? {? ? ? ? ? ? Array.set(newInstance, i, Array.get(obj2, i));? ? ? ? }else{? ? ? ? ? ? Array.set(newInstance, i, Array.get(obj, i - length));? ? ? ? }? ? }returnnewInstance;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ok,這里的兩個(gè)數(shù)組合并狡恬,只需要注意一件事珠叔,將hack_dex.jar里面的dexElements放到新數(shù)組前面即可。
到此弟劲,我們就完成了在應(yīng)用啟動的時(shí)候祷安,動態(tài)的將hack_dex.jar中包含的DexFile注入到ClassLoader的dexElements中。這樣就不會查找不到AntilazyLoad這個(gè)類了兔乞。
ok汇鞭,那么到此呢,還是沒有看到我們?nèi)绾未蜓a(bǔ)丁庸追,哈虱咧,其實(shí)呢,已經(jīng)說過了锚国,打補(bǔ)丁的過程和我們注入hack_dex.jar是一致的腕巡。
你現(xiàn)在運(yùn)行HotFix的app項(xiàng)目,點(diǎn)擊menu里面的測試:
會彈出:調(diào)用測試方法:bug class
接下來就看如何完成熱修復(fù)血筑。
ok,那么我們假設(shè)BugClass這個(gè)類有錯(cuò)誤豺总,需要修復(fù):
packagedodola.hotfix;publicclassBugClass{publicStringbug()? ? {return"fixed class";? ? }}
1
2
3
4
5
6
7
8
9
可以看到字符串變化了:bug class -> fixed class .
然后车伞,編譯,將這個(gè)類的class->jar->dex喻喳。步驟和上面是一致的另玖。
jar cvf path.jar dodola/hotfix/BugClass.class
dx? --dex --output path_dex.jar path.jar
1
2
拿到path_dex.jar文件。
正常情況下表伦,這個(gè)玩意應(yīng)該是下載得到的谦去,當(dāng)然我們介紹原理,你可以直接將其放置到sdcard上蹦哼。
然后在Application的onCreate中進(jìn)行讀取鳄哭,我們這里為了方便也放置到assets目錄,然后在Application的onCreate中添加代碼:
publicclassHotfixApplicationextendsApplication{@OverridepublicvoidonCreate()? ? {super.onCreate();? ? ? ? File dexPath =newFile(getDir("dex", Context.MODE_PRIVATE),"hackdex_dex.jar");? ? ? ? Utils.prepareDex(this.getApplicationContext(), dexPath,"hack_dex.jar");? ? ? ? HotFix.patch(this, dexPath.getAbsolutePath(),"dodola.hackdex.AntilazyLoad");try{this.getClassLoader().loadClass("dodola.hackdex.AntilazyLoad");? ? ? ? }catch(ClassNotFoundException e)? ? ? ? {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? ? ? dexPath =newFile(getDir("dex", Context.MODE_PRIVATE),"path_dex.jar");? ? ? ? Utils.prepareDex(this.getApplicationContext(), dexPath,"path_dex.jar");? ? ? ? HotFix.patch(this, dexPath.getAbsolutePath(),"dodola.hotfix.BugClass");? ? }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
其實(shí)就是添加了后面的3行纲熏,這里需要說明一下妆丘,第一行依舊是復(fù)制到私有目錄锄俄,如果你是sdcard上,那么操作基本是一致的勺拣,這里就別問:如果在sdcard或者網(wǎng)絡(luò)上怎么處理~
ok奶赠,那么再次運(yùn)行我們的app。
ok药有,最后說一下车柠,說項(xiàng)目中有一個(gè)打補(bǔ)丁的按鈕,在menu下塑猖,那么你也可以不在Application里面添加我們最后的3行竹祷。
你運(yùn)行app后,先點(diǎn)擊打補(bǔ)丁羊苟,然后點(diǎn)擊測試也可以發(fā)現(xiàn)成功修復(fù)了塑陵。
如果先點(diǎn)擊測試,再點(diǎn)擊打補(bǔ)丁蜡励,再測試是不會變化的令花,因?yàn)轭愐坏┘虞d以后,不會重新再去重新加載了凉倚。
ok兼都,到此,我們的熱修復(fù)的原理稽寒,已經(jīng)解決方案扮碧,我相信已經(jīng)很詳細(xì)的介紹完成了,如果你有足夠的耐心一定可以實(shí)現(xiàn)杏糙。中間制作補(bǔ)丁等操作慎王,我們的操作比較麻煩,自動化的話宏侍,可以參考https://github.com/jasonross/Nuwa赖淤。
最后就是對于QQ空間團(tuán)隊(duì),以及開源作者的感謝了~~
尊重原作者谅河,轉(zhuǎn)自:http://blog.csdn.net/lmj623565791/article/details/49883661