参考 柯元旦:《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