类加载器(DexClassLoader)与插件化(动态加载)
来源:互联网 发布:佳能ip2880s清零软件 编辑:程序博客网 时间:2024/06/12 01:47
类加载器与插件化解析
2.1 类装载器 DexClassLoader
首先,我们需要了解关于java代码本地import的一些知识: import中所引用的类有两个特点:
1.必须存在于本地,当程序运行时需要该类时,内部类装载器会自动装载该类,这对程序员来讲是透明的,即程序员感知不到该过程
2.编译时必须在现场,否则编译过程会因为找不到引用文件而不能正常编译。
使用ClassLoader的必要说明,多用于动态加载一些自定义的类。
一般情况下,应用程序不需要创建一个全新的ClassLoader,而是使用当前环境中已经存在的ClassLoader。因为Java的Runtime环境在初始化时,其内部会创建一个ClassLoader对象用于加载Runtime所需的各种Java类。
每个ClassLoader必须有一个父ClassLoader,在装载Class文件时,子ClassLoader会先请求其父ClassLoader加载该Class文件,只有当其父ClassLoader找不到该Class时,子ClassLoader才会继续装载该类,这是一种安全机制。
Android使用的是DexClassLoader。
下面简单介绍下DexClassLoader的使用方法
假设这里有两个APK,第一个叫做Host,第二个叫做Plugin,其中Plugin中定义了一个PluginClass,该类中定义了一个函数,functionl(),代码如下:
public class PluginClass { public PluginClass(){ Log.i( "Plugin" , "PluginClass client initialized" ) ; } public int functionl( int a, int b ) { return a + b; } }
我们剩下要做的就是在这个Host APK中去掉用我们PluginClass的functionl()方法。
这里用注释解释下代码
public void useDexClassLoader(){ //确定目标class所在的位置 Intent intent = new Intent("com.haiii.android.plugin.client",null); //通过PackageManager获取信息。 PackageManager pm = getPackageManager(); final List<ResolveInfo> plugins = pm.queryIntentActivities(intent,0); ResolveInfo info = plugins.get(0); ActivityInfo ainfo = info.activityInfo; //当存在多个dexPath路径时,需要此分隔符 String div = System.getProperty("npath.separator"); //报名 String packageName = ainfo.packageName; //目标全路径 String dexPath = ainfo.applicationInfo.sourceDir; //本地解析输出路径 String dexOutputDir = getApplicationInfo().dataDir; //目标lib(C/C++)文件存放路径 String libPath = ainfo.applicationInfo.nativeLibraryDir; //创建我们自己的ClassLoader DexClassLoader cl = new DexClassLoader(dexPath,dexOutputDir,libPath,this.getClass().getClassLoader()); try { //通过反射机制去调用方法 Class<?> clazz = cl.loadClass(packageName+".PluginClass"); Object obj = clazz.newInstance(); Class[] params = new Class[2]; params[0] = Integer.TYPE; params[1] = Integer.TYPE; Method action = clazz.getMethod("functionl",params); Integer ret = (Integer)action.invoke(obj,12,34); Log.i("Host","returnvalueis"+ret); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }catch(Exception e){ Log.e("Host", "Something wrong has happened!"); } }
上面的注释应该挺明确的,这里主要对DexClassLoader的初始化做下说明:
dexPath,指目标类所在的APK或Jar文件的路径。类装载器将从该路径中寻找指定的目标类,该路径必须是APk或Jar的全路径,比如/data/app/com.haiii.android.plugin.apk。如果要包含多个路径,路径之间必须用特定分隔符进行分割,即上面的System.getProperty(“path.sepator”)。
dexOutputDir,由于dex文件被包含在APk或者Jar中,因此在装载目标类之前需要从APk或jar中解压出dex文件,该参数就是指定解压出的dex文件存放路径。在Android系统中,一个应用程序一般对应一个Linux用户id,应用程序仅对属于自己的数据目录路径有写的权限,因此,该参数可以使用该程序的数据路径。
libPath,指目标类中所使用的C/C++库存放路径。
parent,指该装载器的父装载器,一般为当前执行类的装载器。
通过类装载器装载class之后会返回一个class对象,但这个class对象,我们本地是没有对其定义的,所以我们无法使用它的方法,而且此时也没有生成我们需要的PluginClass对象,只是装载了PluginClass的代码而已。所以这里就需要用到反射去创建对象并调用相应的方法。
2.2 基于类装载器设计的”插件“架构
目前市场上也有不少关于插件化的东西,这里将简单介绍下关于使用类装载器实现插件化的思想与方法。
上面的关于DexClassLoader的介绍,我们发现,整个调用都还算简单,但到了最后使用反射这一块太过于繁琐,既然我们知道我们要调用的类和方法,那么为什么不考虑下,使用接口机制呢??相信看这篇文章的人都有一定的Android基础,那么应该都了解service远程aidl机制里,有一个asInterface的方式,我们可以模仿下。
首先定义一个接口,这个接口仅仅定义函数的输入和输出,不定义具体实现。这个接口要同时参与Host与Plugin两个项目(APK)的编译。
//接口定义如下public interface Comm{ public int functionl( int a, int b ) ; }
然后修改下PluginClass的代码
public class PluginClass implements Comm{ public PluginClass(){ Log.i( "Plugin" , "PluginClass client initialized" ) ; } public int functionl( int a, int b ) { return a + b; } }
然后在Host中,我们对于Class对象newInstance()之后返回的对象就可以强制转换为Comm接口对象了,修改下代码:
try{ Class<?> clazz = cl.loadClass(packageName + ".PluginClass"); Comm comm = (Comm)clazz.newInstance(); Integer ret = comm.functionl(12,34); Log.i("Host","return value is " + ret);}
这段代码执行结果与上一段代码的结果是相同的。
我们来看下,当前市场中关于插件的描述:
1.一种逻辑概念,不是技术标准;
2.插件不能独立运行,必须运行与一个宿主程序中,由宿主程序去调用插件程序。
3.宿主程序中可以管理不同的插件,包括数量,禁用或使用,主题
设置等。多个插件,应该能做到切换插件。
4.宿主程序需要保证向下兼容,新版本应该能运行老版本的插件。
注意点:
1.接口一般定义在Host中,如本例的Comm.java。
2.Plugin项目中需要使用Comm是,必须通过一个外部的jar包,这个jar包必须是以Library方式添加到Plugin的build
path中,不能以外部jar包的方式添加,因为我们只需要在Host中存在我们定义的接口,而不想在Plugin中也编译进去,如果都编译进去,就会产生包名相同但验证码不同的文件,导致”Class
ref in pre-verified class resolved to unexpected implementation”.
- 类加载器(DexClassLoader)与插件化(动态加载)
- Android插件化探索(一)类加载器DexClassLoader
- Android插件化探索(一)类加载器DexClassLoader
- Android插件化探索(一)类加载器DexClassLoader
- Android插件化(二):使用DexClassLoader动态加载assets中的apk
- Android插件化(二):使用DexClassLoader动态加载assets中的apk
- DexClassLoader动态加载分析
- 基于类加载DexClassLoader的“插件”结构
- 【Android高级】DexClassloader和PathClassloader动态加载插件的实现
- 通过DexClassLoader动态加载代码
- Android 动态加载之DexClassLoader
- Android插件化开发之DexClassLoader动态加载dex、jar小Demo
- 动态加载与插件化
- 动态加载与插件化
- android动态加载class文件(DexClassLoader可从SD卡加载)
- 加载插件(四)之动态加载
- java通过映射动态加载类 android的DexClassLoader
- 插件化:动态加载类
- 使用Nessus扫描Windows XP SP1之360安全软件
- apache kafka中server.properties配置文件参数说明
- pdf阅读器哪个好
- 关于解决不安全的HTTP方法的验证方案
- 江尤理:稳健盈利的前提是做好投资计划
- 类加载器(DexClassLoader)与插件化(动态加载)
- VC6.0串口编程
- [C++学习笔记]--析构函数、静态成员变量、静态成员函数
- Ubuntu下Eclipse无法正常启动问题解决
- JQuery元素获取常见用法
- Intent的简单介绍
- tomcat solr 部署
- 【Unity】关于音乐音效管理以及dontDestroyOnLoad重复实例化问题
- 杂七杂八