参考 柯元旦:《Android内核剖析》第2.1.1/2.1.2
转载注明出处:http://blog.csdn.net/u012527802/article/details/52620316
在Java环境中,有个概念叫做“类装载器”(ClassLoader),其作用是动态装载Class文件。标准的Java SDK中有一个ClassLoader类,借助它可以装载想要的CLass文件,每个ClassLoader对象在初始化时必须要指定Class文件路径。
在过去的程序开发中,当我们需要某个类是,只需要使用import关键字引入该类就可以。但是import所引用的类文件有两个特点:
- 必须存在于本地,当程序运行需要该类时,内部类装载器会自动装载。
- 编译时必须在场,否则编译过程会因为找不到引用文件而不能正常编译。
在Android应用程序中,虽然使用Java编译Class文件,但是最终的APK文件中包含的确是dex类型的文件。dex文件是将所需要的所有Class文件重新打包,打包的规则不是单纯的压缩。由于dex文件是一种经过优化的Class文件,因此要加载这样特殊的Class文件就要使用Android提供的DexClassLoader。
使用方法及参数说明:
public void useDexClassLoader() {
Intent intent = new Intent("com.lzy.plugin.greet");
PackageManager pm = getPackageManager();
final List<ResolveInfo> plugins = pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER);
if (plugins == null || plugins.size() == 0) {
Toast.makeText(this, "未找到匹配的Plugin!", Toast.LENGTH_LONG).show(); return;
}
ResolveInfo info = plugins.get(0);
ActivityInfo activityInfo = info.activityInfo;
String div = System.getProperty("path.separator"); // 特定分隔符
String packageName = activityInfo.packageName; // plugin包名
String dexPath = activityInfo.applicationInfo.sourceDir; // plugin apk或jar路径
String dexOutputDir = getApplicationInfo().dataDir; // 目标路径
String libPath = activityInfo.applicationInfo.nativeLibraryDir; // 库路径
DexClassLoader classLoader = new DexClassLoader(dexPath, dexOutputDir, libPath, this.getClass().getClassLoader());
try {
Class<?> clazz = classLoader.loadClass(packageName + ".GreetPlugin");
// 方式一
Object instance = clazz.newInstance();
Class[] params = new Class[1];
params[0] = String.class;
Method action = clazz.getMethod("greet", params);
String result = (String) action.invoke(instance, "linzhiyong");
Resources resources = pm.getResourcesForApplication(packageName);
int id = resources.getIdentifier("version", "string", packageName);
Toast.makeText(this, "返回结果:" + result + "\n" + resources.getString(id), Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(this, "" + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
- dexPath,指目标类所在的APK或者Jar文件的路径,类装载器将从该路径中寻找指定的目标类。必须是全路径。
- dexOutputDir,解压出的目标dex文件存放的路径,一般使用本程序的数据路径。
- libPath,指目标类中所使用的C/C++库所存放的路径。
- classLoader,指该装载器的父类装载器,一般为当前执行类的装载器。
反射机制调用时主要使用了Method类,CLass对象的getMethod()方法可以返回该类的任何方法,比如本示例中的greet()方法。getMethod()方法有两个参数,第一个是需要访问的函数名称,第二个是函数参数类型。
得到目标类的函数对象的Method后,就可以调用带Method的invoke()方法去执行目标函数了,invoke()方法的第一个参数是指执行目标函数的对象,该对象就是真正的GreetPlugin对象,就是Class类调用newInstance()创建的。
总结:通过类装载器装载的类,调用其内部函数还是有些繁琐,所以可以通过定义一个interface接口,定义一些函数。该interface同时存在于GreetPlugin项目中,也存在于PluginHost中。在PluginHost去实现该接口,然后在上面示例中使用loadClass直接返回到interface对象。
修改代码如下:
1、在PluginHost中定义interface接口,并且把该类Export为greet.jar包。
package com.lzy.plugin.service;
/**
* Plugin接口
*
* @author lzy
* @time 2016-09-22 10:36:30
* @email wflinzhiyong@163.com
*/
public interface GreetService {
String greet(String greet);
}
2、在GreetPlugin项目中通过Add Library的方式引用greet.jar包(不能使用外部Jar方式原因是:外部Jar会作为程序一部分被打包到apk中,从而使得GreetPlugin和PluginHost项目中存在报名相同但验证码不同的类文件,会在运行时报错“Class ref in pre-verifired class resolved to unexpected implementation”),并且实现interface接口:
package com.lzy.plugin.greet;
import com.lzy.plugin.service.GreetService;
/**
* Plugin接口实现
*
* @author lzy
* @time 2016-09-22 10:36:30
* @email wflinzhiyong@163.com
*/
public class GreetPlugin implements GreetService{
@Override
public String greet(String greet) {
return "GreetPlugin: " + greet;
}
}
3、在 PluginHost修改:
public void useDexClassLoader() {
Intent intent = new Intent("com.lzy.plugin.greet");
PackageManager pm = getPackageManager();
final List<ResolveInfo> plugins = pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER);
if (plugins == null || plugins.size() == 0) {
Toast.makeText(this, "未找到匹配的Plugin!", Toast.LENGTH_LONG).show(); return;
}
ResolveInfo info = plugins.get(0);
ActivityInfo activityInfo = info.activityInfo;
String div = System.getProperty("path.separator"); // 特定分隔符
String packageName = activityInfo.packageName; // plugin包名
String dexPath = activityInfo.applicationInfo.sourceDir; // plugin apk或jar路径
String dexOutputDir = getApplicationInfo().dataDir; // 目标路径
String libPath = activityInfo.applicationInfo.nativeLibraryDir; // 库路径
DexClassLoader classLoader = new DexClassLoader(dexPath, dexOutputDir, libPath, this.getClass().getClassLoader());
try {
Class<?> clazz = classLoader.loadClass(packageName + ".GreetPlugin");
// 方式一
// Object instance = clazz.newInstance();
// Class[] params = new Class[1];
// params[0] = String.class;
// Method action = clazz.getMethod("greet", params);
// String result = (String) action.invoke(instance, "linzhiyong");
// 方式二
GreetService service = (GreetService) clazz.newInstance();
String result = service.greet("linzhiyong");
Resources resources = pm.getResourcesForApplication(packageName);
int id = resources.getIdentifier("version", "string", packageName);
Toast.makeText(this, "返回结果:" + result + "\n" + resources.getString(id), Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(this, "" + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
转载注明出处:http://blog.csdn.net/u012527802/article/details/52620316