DexClassLoader的使用
在Java環境中,有個概念叫做”類裝載器(Class Loader)”,其作用是動態加載Class文件.標準的Java SDK中有一個ClassLoader類,借助他可以裝載想要的Class文件,每個ClassLoader對象在初始化的時候必須指定Class文件的路徑.
但我們在使用java的時候,基本上沒有使用過ClassLoader,僅僅使用import就可以加載類文件了,簡單的講,import中所引用的類文件有兩個特點:
1:必須存在于本地,當程序運行需要該類的時候,內部類裝載器會自動裝載該類,這對程序員來說是透明的,即程序員感知不到該過程
2:編譯時必須在現場,否則編譯過程會因找不到引用文件而不能正常編譯.
但在有些情況下,所需要的類卻不能滿足以上兩個條件.比如當該類是從遠程下載并在本地執行的時候,典型的例子就是通過瀏覽器中的AppletLet執行的java程序,這些要執行的程序是在服務器端.另一種情況是,要引用的Class文件不方便在編譯的時候直接參與,而只能在運行時動態調用.例如,在Android Framework中,所包含的Class文件是一些通用的類文件,但對于一些設備商而言,他們需要擴充Framework,擴充的具體工作包括兩點:
1:需要增加一些額外的類文件,這些類文件提供廠商自定義的功能,這些文件一般以獨立的jar包存在
2:需要修改Framework中的已有的類文件,比如WindowManagerService類,在該類中添加使用自定義jar包中的代碼.使用自定義jar常用的方法是使用import關鍵字包含自定義的類,但為了保持和原生Framework的兼容性,對原聲Framework最少化修改,可以使類裝載器動態裝載自定義jar包.
這就是使用ClassLoader的原因.
在一般情況下,應用程序不需要創建一個全新的ClassLoader對象,而是使用當前環境已經存在的ClassLoader.因為java的Runtime環境在初始化時,其內部會創建一個ClassLoader對象用于加載Runtime所需的各種java類.
每個ClassLoader必須有一個父ClasLoader,在裝載Class文件的時候,子ClassLoader會先請求其父ClassLoader加載該Class文件,只有當其父ClassLoader找不到該Class的時候,子ClassLoader才會急促裝載該類,這是一種安全機制.
對于Android的應用程序,本質上雖然也是用Java開發,并且使用標準的Java編譯器編譯出Class文件,但最終的APK文件中包含的確實dex類型的文件.dex文件是將所需的所有Class文件重新打包,打包的規則不是簡單的壓縮,而是完全對Class文件內部的各種函數表,變量表等進行優化,并產生一個新的文件,這就是dex文件.由于dex文件是一種經過優化的Class文件,因此要加載這樣特殊的Class文件就需要特殊的類裝載器,這就是DexClassLoader.Android SDK中提供了DexClassLoader類就是處于這個目的.
下面我們就來看一下DexClassLoader的調用.
首先我們新建一個Android Project,命名為Plugin.我們的包名設置為:
com.chen.plugin
在我們的包com.chen.plugin中創建一個activity,這個隨便創建,我們用不到這個,該activity的用處僅僅就是用于啟動android程序的.
在包com.chen.plugin中創建一個新的class,命名為PluginClass.
在類PluginClass中我們添加一個函數,名稱為function1(int a,int b).
看一下我們的代碼:
package com.chen.plugin;import android.util.Log;public class PluginClass {public PluginClass(){Log.e("Plugin","PluginClass client initialized");}public int function1(int a,int b){return a+b;} }然后運行我們這個項目,在手機中安裝.
第二步,我們創建一個新的Android Project,命名為Host.同樣創建一個新的Activity.
在MainActivity中添加一個方法,useDexClassLoader(),下面看一下這個方法的具體實現.
public void useDexClassLoader(){Intent intent = new Intent(Intent.ACTION_MAIN,null);intent.addCategory(Intent.CATEGORY_LAUNCHER); PackageManager pm = getPackageManager();final List<ResolveInfo> plugins = pm.queryIntentActivities(intent, 0);ResolveInfo rinfo = null;if(plugins != null){for(int i = 0;i < plugins.size();i++){ResolveInfo r = plugins.get(i);ActivityInfo ainfo = r.activityInfo;String div = System.getProperty("path.seperator");String packageName = ainfo.packageName;if(packageName.equals("com.chen.plugin")){rinfo = plugins.get(i);}}}ActivityInfo ainfo = rinfo.activityInfo;String div = System.getProperty("path.seperator");String packageName = ainfo.packageName;String dexPath = ainfo.applicationInfo.sourceDir;String dexOutputDir = getApplicationInfo().dataDir;String libPath = ainfo.applicationInfo.nativeLibraryDir;DexClassLoader cl = new DexClassLoader(dexPath, dexOutputDir, libPath, this.getClass().getClassLoader());try{Class<?> clazz = cl.loadClass(packageName+".PluginClass");Object obj = clazz.newInstance();Class[] params = new Class[2];params[0] = Integer.TYPE;params[1] = Integer.TYPE;Method action = clazz.getMethod("function1", params);Integer ret = (Integer)action.invoke(obj, 12,34);Log.e("Host","return value is"+ret);}catch(Exception e){Log.e("errpr",e.getMessage());}}在我們的MainActivity的OnCreate()方法中調用該方法,然后在手機中運行Host,就可以看到我們的調試信息.
我們先來看一下結果:
很明顯,Plugin中的函數在Host中被調用了.
下面我們看一下DexClassLoader構造函數的參數的意義:
- 1:dexPath,指目標類所在的APK或jar文件的路徑.類裝載器將從該路徑中尋找指定的目標類,該類必須是APK或jar的全路徑.如果要包含多個路徑,路徑之間必須使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)獲得.
- 2:dexOutputDir,由于dex文件被包含在APK或者Jar文件中,因此在裝載目標類之前需要先從APK或Jar文件中解壓出dex文件,該參數就是制定解壓出的dex 文件存放的路徑.在Android系統中,一個應用程序一般對應一個Linux用戶id,應用程序僅對屬于自己的數據目錄路徑有寫的權限,因此,該參數可以使用該程序的數據路徑.
- 3:libPath,指目標類中所使用的C/C++庫存放的路徑
- 4:最后一個參數是指該裝載器的父裝載器,一般為當前執行類的裝載器
創建了DexClassLoader對象之后,就可以調用loadClass()來裝載指定的類了.該函數返回的是一個Class對象,注意區分Class對象和目標類PluginClass對象,Class對象是ClassLoader所能識別的類,而PluginClass是程序執行后所能識別的類,此時僅僅裝載了PluginClasss的程序代碼,但是還沒有創建 PluginClass對象,因此接下來調用Class對象的newInstance()方法,該方法內部會調用PluginClass的構造函數,并返回i一個真正的PluginClass對象.
雖然生成了PluginClass對象,但是Host本地并沒有其函數,所以只能使用反射機制來調用PluginClass的方法.
關于反射機制,后面文章中會有更加詳細的使用方式.
總結
以上是生活随笔為你收集整理的DexClassLoader的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sql server2008中怎样用sq
- 下一篇: Exchange 2010发现拓扑失败