前言:


上篇说了下java的动态加载机制,最终还是为android的动态加载准备的。android中的动态加载有所不同,android中Dalvik虚拟机所支持的是.dex文件,我们项目中中的代码就是在dex文件中。而ClassLoader运用的也是"双亲代理模式",Android中运用的classloader有2中,分别是--DexClassLoader以及PathClassLoader


两者的区别:


DexClassLoader:用于加载jar/apk/dex,可以加载SD卡中的apk;


PathClassLoader:用于加载被安装过的apk。


不过项目中基本使用的都是DexClassLoader。来看下DexClassLoader的构造函数。


new DexClassLoader(dexPath, optimizedDirectory, libraryPath, parent)


第一个参数:dex/apk/jar压缩文件的路径;


第二个参数:dex解压后存放的路径;


第三个参数:C/C++依赖的本地库文件,可以为null;


第四个参数:上一级的classloader。


DexClassLoader有2中实现方式,一种是通过接口来实现,另一种是通过反射机制来实现。


其他的不多说,先来个实现结果展示,通过DexClassLoader来动态加载我们的class的实现。


android 动态添加module android动态加载dex_java


分别点击两个按钮,一种是通过反射,另一种是通过接口来实现。




以下是我的工程目录:


android 动态添加module android动态加载dex_android 动态添加module_02


所要动态加载的类是我们的ToastImpl.java,而ToastInterface则是ToastImpl实现的接口:




ToastInterface.java:


public interface ToastInterface {
	public String toastString();
}



ToastInterface.java:


public class ToastImpl implements ToastInterface {

	@Override
	public String toastString() {
		// TODO Auto-generated method stub
		return "classLoader successed";
	}

}



然后把我们的ToastImpl.java导出生成.jar文件,再把我们的.jar文件用dx.bat转成我们的Dalvik所识别的.dex文件


android 动态添加module android动态加载dex_加载_03




准备工作已经做好,接下来就是我们的MainActivity的实现了:




public class MainActivity extends Activity {
	
	private Button flect, inter;
	
	// 通过dx.bat生成的dex文件存放的目录,这里我直接放到了工程目录的cache下
	private final static String DEX_PATH = "/data/data/com.example.classloaderforandroid02/cache/target.dex";
	
	// dex解压之后存放的路径,如果是一个固定的路径运行程序的时候会报错:optimizedDirectory not readable/writable
	private File dexOutputDir;
	
	//用于动态加载类的DexClassLoader
	private DexClassLoader classLoader;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Context context = getApplicationContext();
		dexOutputDir = context.getDir("dex", 0);

		/**
		 * 第一个参数:dex压缩文件的路径 
		 * 第二个参数: dex解压之后存放的路径 
		 * 第三个参数: C/C++依赖的本地库文件目录,可以为null
		 * 第四个参数:上一级的classloader
		 */
		classLoader = new DexClassLoader(DEX_PATH,
				dexOutputDir.getAbsolutePath(), null, getClassLoader());
		
		
		flect = (Button) findViewById(R.id.flect);
		inter = (Button) findViewById(R.id.inter);
		
		//反射方式实现
		flect.setOnClickListener(reflectListener);
		//接口方式实现
		inter.setOnClickListener(interfaceListener);
		
		
		
		
	}
	
	/** 通过反射来调用加载到的类*/
	private View.OnClickListener reflectListener = new View.OnClickListener() {
		
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			try {
				Class<?> clazz = classLoader.loadClass("com.ljx.classloader.ToastImpl");
				Method method = clazz.getDeclaredMethod("toastString");
				method.setAccessible(true);
				String result = (String) method.invoke(clazz.newInstance());
				Toast.makeText(getApplicationContext(), result, 0).show();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	};
	
	
	/** 通过接口的方式来调用加载到的类*/
	private View.OnClickListener interfaceListener = new View.OnClickListener() {
		
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			try {
				Class<?> clazz = classLoader.loadClass("com.ljx.classloader.ToastImpl");
				ToastInterface toastIn = (ToastInterface) clazz.newInstance(); 
				String result = toastIn.toastString();
				Toast.makeText(getApplicationContext(), result, 0).show();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	};
}



代码中都有注释,就不详细解释了,不过值得注意的是android中的ClassLoader是通过loadClass来加载类的,内部实现和java中的有所不同,JVM中的classloader是通过defineClass来加载类的,而Dalvik则把这个方法注释掉了。




不过android中动态加载一个类和java中动态加载一个类还有一个更麻烦的地方,比如无法使用被动态记载apk中的resource,以及在创建Activity的时候没有注册到AndroidManifest.xml中,所以在PackageManagerService扫描我们项目apk的时候没有对我们动态加载的Activity进行初始化,导致动态加载Activity无法运行。当然也有解决方法,那就是用一个代理Activity来执行我们动态加载的Activity,或者也可以通过动态创建Activity来执行我们动态加载的Activity,这里仅仅只是提供一个入门级别的动态加载,如果有兴趣的话,可以更深入的了解如何动态创建一个Activity或者创建代理Activity供我们使用。