Android 熱補(bǔ)丁動態(tài)修復(fù)框架小結(jié)

一喘沿、概述

最新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ù)原理

對于熱修復(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的操作

(1)如何去修改一個(gè)類的class文件

這里我們使用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

(2)如何在dx之前去進(jìn)行(1)的操作

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ù)血筑。

五绘沉、完成熱修復(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咱旱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子绷耍,更是在濱河造成了極大的恐慌吐限,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锨天,死亡現(xiàn)場離奇詭異毯盈,居然都是意外死亡剃毒,警方通過查閱死者的電腦和手機(jī)病袄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門搂赋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人益缠,你說我怎么就攤上這事脑奠。” “怎么了幅慌?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵宋欺,是天一觀的道長。 經(jīng)常有香客問我胰伍,道長齿诞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任骂租,我火速辦了婚禮祷杈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘渗饮。我一直安慰自己但汞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布互站。 她就那樣靜靜地躺著私蕾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胡桃。 梳的紋絲不亂的頭發(fā)上踩叭,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音翠胰,去河邊找鬼懊纳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛亡容,可吹牛的內(nèi)容都是我干的嗤疯。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼闺兢,長吁一口氣:“原來是場噩夢啊……” “哼茂缚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屋谭,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤脚囊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后桐磁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悔耘,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年我擂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衬以。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缓艳。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖看峻,靈堂內(nèi)的尸體忽然破棺而出阶淘,到底是詐尸還是另有隱情,我是刑警寧澤互妓,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布溪窒,位于F島的核電站,受9級特大地震影響冯勉,放射性物質(zhì)發(fā)生泄漏澈蚌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一灼狰、第九天 我趴在偏房一處隱蔽的房頂上張望惜浅。 院中可真熱鬧,春花似錦伏嗜、人聲如沸坛悉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裸影。三九已至,卻和暖如春军熏,著一層夾襖步出監(jiān)牢的瞬間轩猩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工荡澎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留均践,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓摩幔,卻偏偏與公主長得像彤委,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子或衡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 好幾個(gè)月之前關(guān)于AndroidApp熱補(bǔ)丁修復(fù)火了一把焦影,源于QQ空間團(tuán)隊(duì)的一篇文章安卓App熱補(bǔ)丁動態(tài)修復(fù)技...
    lgzaaron閱讀 730評論 1 3
  • 針對app線上修復(fù)技術(shù),目前有好幾種解決方案封断,開源界往往一個(gè)方案會有好幾種實(shí)現(xiàn)斯辰。重復(fù)的實(shí)現(xiàn)會有造輪子之嫌,但分析解...
    石先閱讀 5,068評論 2 34
  • 以下內(nèi)容摘錄自Android熱修復(fù)學(xué)習(xí)之旅開篇——熱修復(fù)概述參考:從Java類加載初始化到Android熱修復(fù) Q...
    jtsky閱讀 2,228評論 0 0
  • 參考1參考2參考3參考4 一:熱修復(fù)相關(guān) 熱修復(fù)概念: 以補(bǔ)丁的方式動態(tài)修復(fù)緊急Bug坡疼,不再需要重新發(fā)布App彬呻,不...
    shuixingge閱讀 591評論 0 1
  • 1、我很感恩,一早起來一邊洗漱化妝闸氮,一邊聽到曉梅老師清脆美麗的聲音剪况,真是一種輕而易舉的喜悅!今天早課要點(diǎn):我做任何...
    約慧吧閱讀 371評論 4 1