DexClassLoader的使用
在Java環(huán)境中,有個(gè)概念叫做”類裝載器(Class Loader)”,其作用是動(dòng)態(tài)加載Class文件.標(biāo)準(zhǔn)的Java SDK中有一個(gè)ClassLoader類,借助他可以裝載想要的Class文件,每個(gè)ClassLoader對(duì)象在初始化的時(shí)候必須指定Class文件的路徑.
但我們?cè)谑褂胘ava的時(shí)候,基本上沒有使用過ClassLoader,僅僅使用import就可以加載類文件了,簡(jiǎn)單的講,import中所引用的類文件有兩個(gè)特點(diǎn):
1:必須存在于本地,當(dāng)程序運(yùn)行需要該類的時(shí)候,內(nèi)部類裝載器會(huì)自動(dòng)裝載該類,這對(duì)程序員來說是透明的,即程序員感知不到該過程
2:編譯時(shí)必須在現(xiàn)場(chǎng),否則編譯過程會(huì)因找不到引用文件而不能正常編譯.
但在有些情況下,所需要的類卻不能滿足以上兩個(gè)條件.比如當(dāng)該類是從遠(yuǎn)程下載并在本地執(zhí)行的時(shí)候,典型的例子就是通過瀏覽器中的AppletLet執(zhí)行的java程序,這些要執(zhí)行的程序是在服務(wù)器端.另一種情況是,要引用的Class文件不方便在編譯的時(shí)候直接參與,而只能在運(yùn)行時(shí)動(dòng)態(tài)調(diào)用.例如,在Android Framework中,所包含的Class文件是一些通用的類文件,但對(duì)于一些設(shè)備商而言,他們需要擴(kuò)充Framework,擴(kuò)充的具體工作包括兩點(diǎn):
1:需要增加一些額外的類文件,這些類文件提供廠商自定義的功能,這些文件一般以獨(dú)立的jar包存在
2:需要修改Framework中的已有的類文件,比如WindowManagerService類,在該類中添加使用自定義jar包中的代碼.使用自定義jar常用的方法是使用import關(guān)鍵字包含自定義的類,但為了保持和原生Framework的兼容性,對(duì)原聲Framework最少化修改,可以使類裝載器動(dòng)態(tài)裝載自定義jar包.
這就是使用ClassLoader的原因.
在一般情況下,應(yīng)用程序不需要?jiǎng)?chuàng)建一個(gè)全新的ClassLoader對(duì)象,而是使用當(dāng)前環(huán)境已經(jīng)存在的ClassLoader.因?yàn)閖ava的Runtime環(huán)境在初始化時(shí),其內(nèi)部會(huì)創(chuàng)建一個(gè)ClassLoader對(duì)象用于加載Runtime所需的各種java類.
每個(gè)ClassLoader必須有一個(gè)父ClasLoader,在裝載Class文件的時(shí)候,子ClassLoader會(huì)先請(qǐng)求其父ClassLoader加載該Class文件,只有當(dāng)其父ClassLoader找不到該Class的時(shí)候,子ClassLoader才會(huì)急促裝載該類,這是一種安全機(jī)制.
對(duì)于Android的應(yīng)用程序,本質(zhì)上雖然也是用Java開發(fā),并且使用標(biāo)準(zhǔn)的Java編譯器編譯出Class文件,但最終的APK文件中包含的確實(shí)dex類型的文件.dex文件是將所需的所有Class文件重新打包,打包的規(guī)則不是簡(jiǎn)單的壓縮,而是完全對(duì)Class文件內(nèi)部的各種函數(shù)表,變量表等進(jìn)行優(yōu)化,并產(chǎn)生一個(gè)新的文件,這就是dex文件.由于dex文件是一種經(jīng)過優(yōu)化的Class文件,因此要加載這樣特殊的Class文件就需要特殊的類裝載器,這就是DexClassLoader.Android SDK中提供了DexClassLoader類就是處于這個(gè)目的.
下面我們就來看一下DexClassLoader的調(diào)用.
首先我們新建一個(gè)Android Project,命名為Plugin.我們的包名設(shè)置為:
com.chen.plugin
在我們的包c(diǎn)om.chen.plugin中創(chuàng)建一個(gè)activity,這個(gè)隨便創(chuàng)建,我們用不到這個(gè),該activity的用處僅僅就是用于啟動(dòng)android程序的.
在包c(diǎn)om.chen.plugin中創(chuàng)建一個(gè)新的class,命名為PluginClass.
在類PluginClass中我們添加一個(gè)函數(shù),名稱為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;} }然后運(yùn)行我們這個(gè)項(xiàng)目,在手機(jī)中安裝.
第二步,我們創(chuàng)建一個(gè)新的Android Project,命名為Host.同樣創(chuàng)建一個(gè)新的Activity.
在MainActivity中添加一個(gè)方法,useDexClassLoader(),下面看一下這個(gè)方法的具體實(shí)現(xiàn).
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()方法中調(diào)用該方法,然后在手機(jī)中運(yùn)行Host,就可以看到我們的調(diào)試信息.
我們先來看一下結(jié)果:
很明顯,Plugin中的函數(shù)在Host中被調(diào)用了.
下面我們看一下DexClassLoader構(gòu)造函數(shù)的參數(shù)的意義:
- 1:dexPath,指目標(biāo)類所在的APK或jar文件的路徑.類裝載器將從該路徑中尋找指定的目標(biāo)類,該類必須是APK或jar的全路徑.如果要包含多個(gè)路徑,路徑之間必須使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)獲得.
- 2:dexOutputDir,由于dex文件被包含在APK或者Jar文件中,因此在裝載目標(biāo)類之前需要先從APK或Jar文件中解壓出dex文件,該參數(shù)就是制定解壓出的dex 文件存放的路徑.在Android系統(tǒng)中,一個(gè)應(yīng)用程序一般對(duì)應(yīng)一個(gè)Linux用戶id,應(yīng)用程序僅對(duì)屬于自己的數(shù)據(jù)目錄路徑有寫的權(quán)限,因此,該參數(shù)可以使用該程序的數(shù)據(jù)路徑.
- 3:libPath,指目標(biāo)類中所使用的C/C++庫存放的路徑
- 4:最后一個(gè)參數(shù)是指該裝載器的父裝載器,一般為當(dāng)前執(zhí)行類的裝載器
創(chuàng)建了DexClassLoader對(duì)象之后,就可以調(diào)用loadClass()來裝載指定的類了.該函數(shù)返回的是一個(gè)Class對(duì)象,注意區(qū)分Class對(duì)象和目標(biāo)類PluginClass對(duì)象,Class對(duì)象是ClassLoader所能識(shí)別的類,而PluginClass是程序執(zhí)行后所能識(shí)別的類,此時(shí)僅僅裝載了PluginClasss的程序代碼,但是還沒有創(chuàng)建 PluginClass對(duì)象,因此接下來調(diào)用Class對(duì)象的newInstance()方法,該方法內(nèi)部會(huì)調(diào)用PluginClass的構(gòu)造函數(shù),并返回i一個(gè)真正的PluginClass對(duì)象.
雖然生成了PluginClass對(duì)象,但是Host本地并沒有其函數(shù),所以只能使用反射機(jī)制來調(diào)用PluginClass的方法.
關(guān)于反射機(jī)制,后面文章中會(huì)有更加詳細(xì)的使用方式.
總結(jié)
以上是生活随笔為你收集整理的DexClassLoader的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sql server2008中怎样用sq
- 下一篇: Exchange 2010发现拓扑失败