android熱更新檔初探----HotFixDemo
1.先來個代碼結構
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jM4gzNzgzM0EjNyIDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
2.熱更新檔的文章有好多了,我這裡就不贅述了。
來兩個連結
QQ空間 的連結
https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a&scene=1&srcid=1106Imu9ZgwybID13e7y2nEi#wechat_redirect
這還有另一片相關的文章
http://blog.csdn.net/lmj623565791/article/details/49883661
理論的學習還是要很多的,不然各種搞不懂,不知道都是什麼東西,大概有哪些步驟也是不清楚的。
hotfix github位址:
https://github.com/dodola/HotFix
3.這裡大概總結下有哪些主要的步驟:
1.android編譯生成apk後,apk解壓後會有一個classes.dex 檔案,android系統在加載這個dex檔案時,是可以加載多個dex檔案的。
2.我們可以再編譯一個dex檔案傳到使用者正在使用的app中,
3.加載到APP指定的目錄,/data/data/包名/app_檔案夾名/
4.然後将這個dex也加載到系統中,執行最新的dex中的class代碼,把最原始的類覆寫掉。
4.其中比較關鍵的技術點
1.加載dex的時候,加載了多個是有加載順序的,需要把後傳上去的更新檔包先去查找其中的類。
如: 原始dex 中的 Bug.class
更新檔dex 中的 Bug.class
這兩個需要設定成加載更新檔包中Bug.class先加載
2.android在編譯應用的時候,預設是隻加載原始dex的,如果dex中有對應的類Bug.class 就會在這個類上打個标志CLASS_ISPREVERIFIED,
Bug.class 被打上這個标志後,這個類就不會在去找其他的dex中的Bug.class了(編譯系統優化産生的這個标志)。
怎麼樣才能不打這個标志呢?
hotfix中采用的做法是:在類加載的時候,在類的構造方法中加載另外一個dex檔案。就是demo中的hackdex_dex.jar,這個實際是個dex檔案。
這個類在應用初始化的時候Application中執行加載這個dex.,在對應的其他所有類中的構造方法中都加載這個中間dex其中的一個類即可,不用做事情。
就是為了消除這個CLASS_ISPREVERIFIED标志,這樣就不打标了。
注意:打更新檔這裡,有三個dex檔案,
第一個是原始apk中帶的classes.dex檔案,
第二個是這個解除打标用的dex檔案,這個dex檔案中,有一個空的類的就ok。
第三個就是更新檔的dex檔案。
此步驟的實作相對麻煩些:
要在gradle中處理一些東西,即在編譯apk的時候要将所有要打更新檔的類的構造方法中打上一句加載第二個dex檔案的話
例如:System.out.print(SecondDexClass.class);
注意:這句話不能在系統初始化的自定義Application中執行。(為什麼這裡不說了,去其他的部落格看一下吧,要有理論支撐)
是以這要對gradle進行程式設計,hotfix demo中已經有寫了,可以參考。
3.就是從伺服器或者手機sd卡的某個位置獲得更新檔dex檔案,然後執行加載。
理論就大概介紹到這裡。
這個是代碼位址:
http://download.csdn.net/detail/methods2011/9444827
下面對代碼進行簡單的介紹
1.首先要有bug類,列印的bug class000就是需要更新檔 改變的
public class BugClass {
public String bug() {
return "bug class000";
}
}
2.然後系統初始化的Application,
這裡進行加載了第二個dex檔案
dodola.hackdex.AntilazyLoad
這個類為第二個dex中的唯一的一個空類
public class HotfixApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
File dexPath = new File(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();
}
}
}
3.這個是在Activity中執行的代碼片段
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
//準備更新檔,從assert裡拷貝到dex裡
File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "modify_dex.jar");
Utils.prepareDex(this.getApplicationContext(), dexPath, "modify_dex.jar");
// DexInjector.inject(dexPath.getAbsolutePath(), defaultDexOptPath, "dodola.hotfix
// .BugClass");
HotFix.patch(this, dexPath.getAbsolutePath(), "com.method.hotfixdemo_01.BugClass");
return true;
}
if (id == R.id.action_test) {
LoadBugClass bugClass = new LoadBugClass();
Toast.makeText(this, "測試調用方法:" + bugClass.getBugString(), Toast.LENGTH_SHORT).show();
return true;
}
modify_dex.jar是加載的更新檔dex檔案其中隻有一個bugClass檔案,其中反編譯的
如果更新檔成功的話:會toast 這個。。。
4.我把另外兩個dex(非原始apk帶的)放到了assets中。
5.有一個groovy子產品,用于gradle編譯更新檔類用的。
build.gradle中的設定是這樣的
apply plugin: 'groovy'
repositories {
mavenCentral()
}
dependencies {
compile gradleApi()
compile localGroovy()
compile 'org.javassist:javassist:3.18.+'
}
注意最上面一行的這個:apply plugin: 'groovy'
這個下來大緻就是可以跑的有bug的app了。
然後是更新檔的打包的過程。
打完包的名稱是:
modify_dex.jar
實際是一個dex檔案
1:将BugClass類修複下
然後找到對應編譯後的BugClass.class,要反編譯下看看這個檔案更新了沒,
之後建立一個檔案夾,将這個類對應的包名都弄對,然後将這個BugClass.class檔案放入其中。
modify.jar就是 用指令行執行 jar -cvf modify.jar *
modify_dex.jar 使用android\sdk\build-tools\23.0.2\dx.bat 檔案進行編譯的
dx --dex --output=modify_dex.jar modify.jar
之後這個dex就可以最為更新檔送到正在使用的app中了,代碼自行補腦了。
然後就是代碼中的執行這個:
File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "modify_dex.jar");
Utils.prepareDex(this.getApplicationContext(), dexPath, "modify_dex.jar");
// DexInjector.inject(dexPath.getAbsolutePath(), defaultDexOptPath, "dodola.hotfix
// .BugClass");
HotFix.patch(this, dexPath.getAbsolutePath(), "com.method.hotfixdemo_01.BugClass");
然後這個bug就被解決了。
ok!