天天看點

android熱更新檔初探

android熱更新檔初探----HotFixDemo

1.先來個代碼結構

android熱更新檔初探

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檔案,其中反編譯的

android熱更新檔初探

如果更新檔成功的話:會toast 這個。。。

4.我把另外兩個dex(非原始apk帶的)放到了assets中。

android熱更新檔初探

5.有一個groovy子產品,用于gradle編譯更新檔類用的。

android熱更新檔初探

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類修複下

android熱更新檔初探

然後找到對應編譯後的BugClass.class,要反編譯下看看這個檔案更新了沒,

android熱更新檔初探

之後建立一個檔案夾,将這個類對應的包名都弄對,然後将這個BugClass.class檔案放入其中。

android熱更新檔初探

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!

繼續閱讀