天天看點

Android應用程式插件化研究之DexClassLoader

文章首發:[android應用程式插件化研究之dexclassloader|大利貓](http://www.liuguangli.win/archives/366)

最近在研究android應用的插件化開發,看了好幾個相關的開源項目,插件化都是在解決以下幾個問題:

* 如何把插件apk中的代碼和資源加載到目前虛拟機。

* 如何把插件apk中的四大元件注冊到程序中。

* 如何防止插件apk中的資源和宿主apk中的資源引用沖突。

圍繞着這幾個問題,我将會寫系列文章逐漸分析插件化的原理和實作方案。本篇文章主要講第一點:如何加載另一個apk中的class。我們要把一個包含class檔案的jar加載到java虛拟機中,需要使用classloader這個類。android的編譯系統中對class檔案進行了進一步的處理:最後變成 .dex檔案,.dex檔案包含在apk中,google提供了一個類來加載.dex檔案,這個類就是dexclassloader,它繼承自classloader。本篇的重點是寫一個例子來說明dexclassloader的用法。先來熟悉下dexclassloader。

# dexclassloader介紹

dexclassloader是一個類加載器,可以用來從.jar和.apk檔案中加載class。可以用來加載執行沒用和應用程式一起安裝的那部分代碼。構造函數:dexclassloader(string dexpath, string optimizeddirectory, string librarypath, classloader parent)

<br>dexpath:被解壓的apk路徑,不能為空。

<br>optimizeddirectory:解壓後的.dex檔案的存儲路徑,不能為空。這個路徑強烈建議使用應用程式的私有路徑,不要放到sdcard上,否則代碼容易被注入攻擊。

<br>librarypath:os庫的存放路徑,可以為空,若有os庫,必須填寫。

<br>parent:父親加載器,一般為context.getclassloader(),使用目前上下文的類加載器。

關于dexclassloader更深入的了解,我建議讀者先去研究一下java的累加載器,然後在去讀一讀dexclassloader的源碼,本章不做深究,接下來寫一個demodex來示範classloader的使用。

# 建立一個插件apk工程module:bundle

我把插件的包名命名為:com.dexclassdemo.liuguangli.apkbeloaded,我們建立一個類:classtobeload:

    package com.dexclassdemo.liuguangli.apkbeloaded;

      import android.util.log;

      public class classtobeload {    

      public  void method(){      

            log.v("classtobeload", "called method of class " +       classtobeload.class.getname());  

      }

    }

我們會示範這個方法如何在宿主中被調用的,并且我們可以跟蹤這個類的類加載器。我們編譯這個工程得到的ask檔案為:apkbeloaded-debug.apk。

# 建立一個宿主apk工程

我把宿主包名命名為:dexclassloaderdemo。我們在assets目錄下建立一個目錄plugins,然後把apkbeloaded-debug.apk拷貝到該目錄下。在mainactivity中建立一個方法為:loadapk

    public  void loadapk(string apkpath) {   

     log.v("loaddexclasses", "dex preparing to loaddexclasses!");   

     file dexopt = this.getdir("dexopt", mode_private);       

     final dexclassloader classloader = new dexclassloader(                    apkpath,               

     dexopt.getabsolutepath(),      

          null,               

     this.getclassloader());     

       log.v("loaddexclasses", "searching for class : "                + "com.registry.registry");       

     try {        

        class<?> classtoload = (class<?>)     classloader.loadclass("com.dexclassdemo.liuguangli.apkbeloaded.classtobeload");          

      object instance = classtoload.newinstance();         

       method method = classtoload.getmethod("method");    

            method.invoke(instance);   

     } catch (illegalaccessexception e) { 

           e.printstacktrace();     

       } catch (classnotfoundexception e) {   

         e.printstacktrace();      

      } catch (nosuchmethodexception e) {     

      } catch (invocationtargetexception e) {    

        e.printstacktrace();     

       } catch (instantiationexception e) {         

        e.printstacktrace();       

     }

我們從apkpath讀取檔案加載(本例中拷貝到sdcard,實際開發中最好拷貝到私有目錄,防止被注入)。

最後我們來再 oncreate 方法中調用  loadapk :

    @override

    protected void oncreate(bundle savedinstancestate) {       

     super.oncreate(savedinstancestate);    

     setcontentview(r.layout.activity_main);  

      string apkpath = environment.getexternalstoragedirectory() + "/bundle.apk";    loadapk(apkpath);

    }  

編譯 bundle 工程,然後把邊緣後的 apk 檔案拷貝到測試機的 sdcard 目錄下,更名為 bundle.apk。 然後運作宿主 apk 驗證結果。

demo源碼

下載下傳源碼:[https://github.com/liuguangli/dexclassloaderdemo](https://github.com/liuguangli/dexclassloaderdemo)

下篇文章研究:[如何加載插件中的資源](http://www.jianshu.com/p/c8c03bdd11e3)

繼續閱讀