基于类加载DexClassLoader的“插件”结构

这篇文章主要讲类加载器在android中如何动态的加载其他工程类的过程,对于类加载器的知识就跳过了。

1、首先需要创建两个工程,我创建的工程是classloader和classloaderplugin,前面的工程是主工程,后面是插件。现在classloader工程需要调用classloaderplugin插件中的类中的方法。在这里使用反射就能解决此问题。

1.1 首先看下classloaderplugin中类的方法,很简单返回两个数的和。

public class PluginClass implements Comm{@Overridepublic int add(int a, int b) {return a+b;}}

1.2 创建类加载器和调用类里面的方法

这个com.test.lb.plugin.classloaderplugin过滤器(IntentFilter)是配置在classloaderplugin项目中的,通过它就可以获取手机上匹配的插件项目,这里就能获取classloaderplugin,继而可以该项目的包名,路径等。

private DexClassLoader createWrapperClassLoader(){try {//intent 过滤插件Intent intent = new Intent("com.test.lb.plugin.classloaderplugin",null);PackageManager pm = getPackageManager();final List<ResolveInfo> plugins = pm.queryIntentActivities(intent, 0);ResolveInfo rInfo = plugins.get(0);ActivityInfo activityInfo = rInfo.activityInfo;//String div = System.getProperty("path.separator");packageName = activityInfo.packageName;String dexPath = activityInfo.applicationInfo.sourceDir;String dexOutputDir = getApplicationInfo().dataDir;String libPath = activityInfo.applicationInfo.nativeLibraryDir;return new DexClassLoader(dexPath, dexOutputDir, libPath, this.getClass().getClassLoader());} catch (Exception e) {e.printStackTrace();}return null;} 拿到类加载器cl之后,就可以加载类了,这里需要传入类的包名。通过newInstance创建一个对象,,然后获取方法getMethod,第二个参数就是所调用方法的参数签名,最后通过invoke方法获取结果。运行时先要安装classloaderplugin即插件,然后在安装classloader主项目。Class<?> clazz = cl.loadClass(packageName+".plugin.PluginClass");Object obj = clazz.newInstance();Class[] parameterTypes = new Class[2];parameterTypes[0] = Integer.TYPE;parameterTypes[1] = Integer.TYPE;Method method = clazz.getMethod("add", parameterTypes);tv_reflect_result.setText("反射调用: 20+30= "+method.invoke(obj, 20,30));

1.3 接口模型的“插件”

上面是通过反射调用方法来实现的,接着采用接口方法来实现。

首先创建一个接口,它位于classloader项目中,然后将改接口编译成一个jar包,在将这个jar包放到classloaderplugin中以library的形式添加到build path 中。接口定义如下:

public interface Comm {public int add(int a, int b);}创建类加载和1.2都是一样的,不同的事这次在newInstance时直接进行强制转换,Comm comm = (Comm) clazz.newInstance();直接转换成Comm接口,然后就直接调用里面的方法。/** * 通过反射创建出对象然后将其强制转换成接口类型 */private void useDexClassLoaderIntf(){try {Class<?> clazz = cl.loadClass(packageName+".plugin.PluginClass");Comm comm = (Comm) clazz.newInstance();tv_result.setText("接口调用: 20+30= "+comm.add(20,30));} catch (Exception e) {e.printStackTrace();}}1.4 项目结构

1.5 基于上面的模式,写了一个简单的切换主题的效果

基本思路是,根据包名获取Resources对象,在通过类加载的创建一个对象并强制将其转换成颜色id的接口,在通过Resources的getColor方法获取颜色值。获取颜色的接口如下:第一个是背景色,第二个是文本的颜色,第三个是文本背景色。

public interface ColorIntf {public int getBackgroundColorId();public int getTextFontColorId();public int getTextBackgroundColorId();}在两个项目classloader和classloaderplugin中分别加入颜色值如: <color name="background">#cdcdf5</color><color name="textbackground">#FF55bb</color><color name="text">#ccffaf</color> <color name="background">#CDCDCD</color><color name="textbackground">#FF5555</color><color name="text">#000000</color>

在调用下面的方法给各个空间赋值,会根据传入不同的包名去加载不同的颜色实现类。

private void useDexClassLoaderColorIntf(String packagename){try {Class<?> clazz = cl.loadClass(packagename+".plugin.ColorClass");ColorIntf colorIntf = (ColorIntf) clazz.newInstance();Resources resources = readResources(packagename);tv_result.setTextColor(resources.getColor(colorIntf.getTextFontColorId()));tv_result.setBackgroundColor(resources.getColor(colorIntf.getTextBackgroundColorId()));ll_main.setBackgroundColor(resources.getColor(colorIntf.getBackgroundColorId()));tv_reflect_result.setTextColor(resources.getColor(colorIntf.getTextFontColorId()));tv_reflect_result.setBackgroundColor(resources.getColor(colorIntf.getTextBackgroundColorId()));} catch (Exception e) {e.printStackTrace();}}1.6 最后效果图有人要进来,有一些人不得不离开。

基于类加载DexClassLoader的“插件”结构

相关文章:

你感兴趣的文章:

标签云: