首先我们不谈什么是“插件”模式,先了解下DexClassLoader类,该类是android用来加载dex文件使用的,可以动态加载class类文件,我们称之为“类装载器”。
假设我们有两个APK,一个是Host,一个是Plugin,其中Plugin中有个PluginClass类,该类有个方法function1(),代码如下:
package com.ldw.plugin;
import android.util.Log;
public class PluginClass {
public PluginClass() {
Log.i("Plugin", "PluginClass client initialized");
}
public int function1(int a, int b) {
return a + b;
}
}
其中Host中MainActivity代码如下:
package com.ldw.host;
import java.lang.reflect.Method;
import java.util.List;
import dalvik.system.DexClassLoader;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
useDexClassLoader();
}
@SuppressLint("NewApi")
public void useDexClassLoader() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setPackage("com.ldw.plugin");
PackageManager pm = getPackageManager();
List<ResolveInfo> plugins = pm.queryIntentActivities(intent, 0);
ResolveInfo rinfo = plugins.get(0);
ActivityInfo ainfo = rinfo.activityInfo;
String packageName = ainfo.packageName;
//目标类所在apk或jar包文件的路径
String dexPath = ainfo.applicationInfo.sourceDir;
//由于dex文件存在apk中,因此在装载目标类之前需要先从apk中解压出dex文件,这个参数就是解压后存放的路径
String dexOutputDir = getApplicationInfo().dataDir;
//指目标类中所使用的C/C++库存放的路径
String libPath = ainfo.applicationInfo.nativeLibraryDir;
DexClassLoader cl = new DexClassLoader(dexPath, dexOutputDir, libPath, this.getClass().getClassLoader());
try {
Class<?> clazz = cl.loadClass(packageName + ".PluginClass");
//获取真正的PluginClass对象
Object obj = clazz.newInstance();
Class[] params = new Class[2];
params[0] = Integer.TYPE;
params[1] = Integer.TYPE;
Method action = clazz.getMethod("function1", params);
Integer ret = (Integer) action.invoke(obj, 12, 12);
Log.i("Host", "result is " + ret);
} catch (Exception e) {
Log.i("Host", "error", e);
}
}
}
上面useDexClassLoader()方法主要介绍了如何使用DexClassLoader来动态加载外部apk中的方法。
有人可能感觉使用动态加载来加载外部类中的方法的过程有些繁琐,构造Method,构造参数,然后才能调用,有没有一种方法直接newInstance()出来类对象之后就可以调用类里面的方法呢,答案是肯定的。我们可以首先定义一个interface接口,该接口中有方法function1(),注意该接口是在Host工程中定义的,然后我们导出一个jar包,该jar包很简单,只包含我们定义的那个接口类,然后我们将该jar包导入到我们的Plugin工程中,然后类PluginClass实现该接口,注意导入到Plugin的方式,如图所示:
图中错误的导入方式的原因是因为如果以外部jar的形式导入的话会作为程序的一部分被打包到最终的程序文件中,从而使得Plugin和Host项目中存在包名相同但验证码不同的类文件,这最终会导致运行时出现错误信息“Class ref in pre-verified class resolved to unexpected implementation”。
这里我就不再演示代码了,下一节将介绍使用“插件”模式定制皮肤的功能。

找了好多关于这个问题的文章 终于找到原因了 原来就这么简单 蛋都碎了一地啊。。。