android DexClassLoader动态加载技术详解

  • Post author:
  • Post category:其他


介绍

做项目到一定庞大的时候就会发现方法数太多,安装包根本就装不上去了,这个也不足为奇,我们都知道当方法数目

超过65536



这个数目限制的时候,挡在2.x的系统上面就会出现无法安装的情况,这个时候动态加载技术就显得非的重要了,我们的项目中为了兼容2.x的手机也是用到了android的动态加载技术,这里我会详细的讲解一下怎么去用,怎么实战,我感觉,空谈理论不如动手来得实在。



实例




下面就通过一个例子反复的说明怎么来实现动态加载,通过不同的方法来调用。



准备工作






1:新建一个java工程(我比较懒我就直接新建Android工程了)





2:然后看看包路径。









我们敲入里面的代码:先是Iinterface.java




package com.demo.dex;

/**
 * 对外接口
 * 
 * @author edsheng
 * 
 */
public interface Iinterface {
	public void call();

	public String getData();
}

然后是Iclass.java




package com.demo.dex;

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

public class IClass implements Iinterface {

	private Context context;

	public IClass(Context context) {
		super();
		this.context = context;
	}

	@Override
	public void call() {
		Toast.makeText(context, "call method", 0).show();
	}

	@Override
	public String getData() {
		return "hello,i am from IClass";
	}

}

注意上面的实现类,在构造的时候我给它传递了以Context对象,为什么要这样呢?因为在android里面什么东西不都是通过context来获取的吗?我这里为了实验就只调用了一下toast。



既然准备工作都准备好了,那就开始下一步吧。导出为jar。工程上面右键,export->java->jar file选中src下面的这两个类就行了。








导出我们的jar文件我给他命名为test.jar,然后把java的class文件转换成android能识别的字节码吧。使用dx工具,可以在andorid的SKD目录下面搜索一下这个工具,以前的说在platform-tools这个目录下面但是我没有找到,相反我在build-tools里面的子目录里面找到了不同版本的dx工具,我随便找了一个版本的19.0.3然后把test.jar复制到这个目录下面, 在cmd命令里面敲入

dx --dex --output=testdex.jar test.jar



然后准备工作就完成了,这样一个优化的jar或者说是dex就准备好了。



正式开始




新建一个andorid工程(我比较懒,刚才已经就是使用的andorid工程),然后把testdex.jar复制到assets目录下面,来看看我的工程目录吧。


然后贴出FileUtil.java的代码

package com.demo.utile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

import android.content.Context;
import android.os.Environment;

public class FileUtile {
	public static void CopyAssertJarToFile(Context context, String filename,
			String des) {
		try {

			File file = new File(Environment.getExternalStorageDirectory()
					.toString() + File.separator + des);
			if (file.exists()) {
				return;
			}

			InputStream inputStream = context.getAssets().open(filename);
			file.createNewFile();
			FileOutputStream fileOutputStream = new FileOutputStream(file);
			byte buffer[] = new byte[1024];
			int len = 0;
			while ((len = inputStream.read(buffer)) != -1) {
				fileOutputStream.write(buffer, 0, len);
			}
			fileOutputStream.close();
			fileOutputStream.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

这个类主要的作用就是把我们的jar相当于解压到一个目录下面。我这里是解压到外置设备上的其实这样做的安全性并不高,但是为了方便我就这样做了,建议是解压到包目录下面。记住别忘了给应用加权限!!!!!文件读写权限!!!!



最后我们来看看Activity里面的代码

package com.demo.activity;

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

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;

import com.demo.utile.FileUtile;

import dalvik.system.DexClassLoader;

public class MainActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		FileUtile.CopyAssertJarToFile(this, "testdex.jar", "testdex.jar");

		File file = new File(Environment.getExternalStorageDirectory()
				.toString() + File.separator + "testdex.jar");
		final File optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE);
		DexClassLoader classLoader = new DexClassLoader(file.getAbsolutePath(),
				optimizedDexOutputPath.getAbsolutePath(), null,
				getClassLoader());
		try {
			Class<?> iclass = classLoader.loadClass("com.demo.dex.IClass");
			Constructor<?> istructor = iclass.getConstructor(Context.class);
			//利用反射原理去调用
			Method method = iclass.getMethod("call", null);
			String data = (String) method.invoke(istructor.newInstance(this), null);
			System.out.println(data);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

其实这样调用还有一点要注意的是我感觉这样做每次都要去调用反射,总给我感觉来说不太友好,那么这里我有给出了下面一种写法,这种写法最主要的地方是,要获得Iinterface这个接口,把Iinterface.java这个文件复制到你的工程里面,记得包名相同,调用的时候我们可以这样来调用它。




Class<?> iclass = classLoader.loadClass("com.demo.dex.IClass");
			Constructor<?> istructor = iclass.getConstructor(Context.class);
			Iinterface iinterface = (Iinterface) istructor.newInstance(this);
			 String data = iinterface.getData();
			 iinterface.call();
			 Toast.makeText(this, data, 0).show();

这样是不是看起来更舒服呢?



好了今天就分享到这里。Demo下载地址:CSDN上传抽风了,这里先上到百度网盘

点击这里







版权声明:本文为shengbo1992原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。