天天看點

安卓實作動态加載class

前兩天與老師讨論到指令模式的使用,提到了類的動态加載,即從外部加載進class。動态加載在之前有看到過,但沒有留意,于是今天特意寫了個demo測試一下。首先介紹一下類加載器的概念,内容由百度而來。

類加載器的基本概念

  類加載器(class loader)用來加載 Java 類到 Java 虛拟機中。一般來說,Java 虛拟機使用 Java 類的方式如下:Java 源程式(.java )在經過 Java 編譯器編譯之後就被轉換成 Java 位元組代碼(.class )。類加載器負責讀取 Java 位元組代碼,并轉換成 java.lang.Class 類的一個執行個體。每個這樣的執行個體用來表示一個 Java 類。通過此執行個體的 newInstance()方法就可以建立出該類的一個對象。實際的情況可能更加複雜,比如 Java 位元組代碼可能是通過工具動态生成的,也可能是通過網絡下載下傳的。

類加載器的樹狀組織結構

  Java 中的類加載器大緻可以分成兩類,一類是系統提供的,另外一類則是由 Java 應用開發人員編寫的。系統提供的類加載器主要有下面三個:

  引導類加載器(bootstrap class loader):它用來加載 Java 的核心庫,是用原生代碼來實作的,并不繼承自 java.lang.ClassLoader。

  擴充類加載器(extensions class loader):它用來加載 Java 的擴充庫。Java 虛拟機的實作會提供一個擴充庫目錄。該類加載器在此目錄裡面查找并加載 Java 類。

  系統類加載器(system class loader):它根據 Java 應用的類路徑(CLASSPATH)來加載 Java 類。一般來說,Java 應用的類都是由它來完成加載的。可以通過 ClassLoader.getSystemClassLoader() 來擷取它。

  除了系統提供的類加載器以外,開發人員可以通過繼承 java.lang.ClassLoader 類的方式實作自己的類加載器,以滿足一些特殊的需求。

需要注意的是,安卓中開發人員可用的類加載隻有DexClassLoader和PathClassLoader。并且後者隻能加載已經安裝了的apk中的class。今天我用到的是DexClassLoader,實作從SD卡中加載class。demo中還用到了反射,對反射不熟悉的朋友可以百度“java 反射”。

首先建立一個安卓工程,按照平時的步驟就可以了,package我設定為com.everlin.loadtest。然後在工程中建立一個包路徑:com.everlin.loadtest.jar,在這個包中建立一個JarTest類:

package com.everlin.loadtest.jar;

import android.content.Context;
import android.widget.Toast;

public class JarTest {
	public static void show(Context context){
		Toast.makeText(context, "加載成功!", Toast.LENGTH_LONG).show();
	}
}
           

接着将該包導出為jar包:在項目浏覽器主公點選選擇包》File》Export》java》jar file》next》填寫導出路徑》finish。我導出的檔案為test.jar

導出後要将jar包轉換為dex:把剛才導出的jar檔案複制到你安卓sdk安裝路徑下的"build-tools\版本\"檔案夾下,我電腦的路徑是“D:\Program Files\eclipse\android_sdk\build-tools\18.1.0”。

然後在該路徑打開指令行視窗,輸入“dx --dex --output=儲存路徑 源路徑”。如我的是:“dx --dex --output=my.jar test.jar”。完成後會在這個路徑下生成my.jar檔案,這個就是處理後的dex。把他複制到模拟器的檔案系統中,我放在了“sdcard\everlin\my.jar”。

接下來重新進入eclipse,把剛才那個包删了,目的是為了效果明顯,避免讀者認為我們待會載入的是apk中的包。然後修改剛才自動生成的MainActivity類:

</pre><p><pre name="code" class="java">package com.everlin.loadtest;

import java.io.File;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//檔案系統根目錄
		String rootPath = Environment.getExternalStorageDirectory().toString();
		//我放置dex檔案的路徑
		String dexPath = rootPath + File.separator + "everlin" + File.separator
				+ "my.jar";
		//執行個體一個DexClassLoader,參數1為檔案路徑,參數2為處理路徑。
		//我試了用檔案系統卡的路徑,但報錯說路徑不屬于目前使用者。查了資料說是因為内置存儲的原因。
		//于是改成apk的安裝路徑,就正常了。
		DexClassLoader cl = new DexClassLoader(dexPath, getDir("dex", 0)
				.getAbsolutePath(), null, getClassLoader());

		try {
			//加載類
			Class<?> test = cl.loadClass("com.everlin.loadtest.jar.JarTest");
			//利用反射,擷取這個類的方法
			Method[] methods = test.getDeclaredMethods();
			//執行第一個方法,即JarTest.show()
			methods[0].invoke(null,getApplication());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
           

調試工程,模拟器運作結果如下:

安卓實作動态加載class