类加载器与插件化解析

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 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”.

java类加载器 架构 设计_类加载器(DexClassLoader)与插件化(动态加载)相关推荐

  1. [JAVA冷知识]动态加载不适合数组类?那如何动态加载一个数组类?

    写在前面 今天和小伙伴分享一些java小知识点,主要围绕下面几点: 既然数组是一个类, 那么编译后类名是什么?类路径呢? 为什么说动态加载不适合数组? 那应该如何动态加载一个数组? 部分内容参考 &l ...

  2. java动态加载类 框架_java运用RMI框架类的动态加载不成功

    最近在看<java网络编程精解>,第11章中用RMI动态加载类,可怎么试都不成功,求大神解答 1. 我先在C盘下启动了rmiregistry注册表,如果部署成功注册表应该会从codebas ...

  3. java 父委托机制优点_类加载器及父亲委托机制

    一.类加载器 1.在Java中,有两种类型的类加载器,分别是JVM自带的类加载器和用户自定义的类加载器. 2.JVM自带的类加载器有三种,如下: 根(Bootstrap)类加载器:该加载器没有父加载器 ...

  4. 【Android 逆向】类加载器 ClassLoader ( 使用 DexClassLoader 动态加载字节码文件 | 拷贝 DEX 文件到内置存储 | 加载并执行 DEX 字节码文件 )

    文章目录 一.拷贝 Assets 目录下的 classes.dex 字节码文件到内置存储区 二.加载 DEX 文件并执行其中的方法 三.MainActivity 及执行结果 四.博客资源 一.拷贝 A ...

  5. ASP.NET Web Game 架构设计1--服务器基本结构

    ASP.NET Web Game 架构设计1--服务器基本结构 1.     基本结构图 2.      系统组成与角色 整个系统大体上分为三个部分:1.网页客户端.2.IIS Web服务器.3.数据 ...

  6. 7种主流案例,告诉你调度器架构设计通用法则(干货!)

    女主宣言 今天小编为大家转载一篇来自DBAplus社群的干货文章,希望能够帮助大家对关于调度器的理解.作者张晨,Strikingly数据平台工程师,算法.分布式系统和函数式编程爱好者.Shanghai ...

  7. java开发架构设计_跪了!阿里技术官出品:Java架构设计之完美,看完秒进大厂。...

    写在前面 "给我一个支点,我就能撬起地球".关键不在于力量有多大,而在于如何合理地利用力量.软件设计同样如此.思想的确立,技巧的把握,将在很大程度上决定软件架构的合理性.内容涵盖了 ...

  8. 软硬整合商业思维与架构设计_答问集

    欢迎访问 ==>高老师的博客网页 高焕堂:MISOO(大数据.大思考)联盟.台北中心和东京(日本)分社.总教练 EE                                       ...

  9. 高老师的架构设计_隽语集(CC_1201)

    前言: 平台(操作系统)是轿子,不一定要自己做轿子,会做好轿子的人处处都有,但是要我们自己能坐上轿子爽快一下才算数:而且坐上去之后,有人争先恐后来抬轿才算成功.如何忽悠别人来大力抬轿(且心甘情愿)是心 ...

最新文章

  1. Utilize Sql Tuning Advisor from Script
  2. 非官方影印版存在的问题
  3. 数据库存储图片路径并显示图片
  4. C/C++教程 第十二章 —— MFC的基本使用
  5. 基因本体论GO(Gene Ontology)
  6. 无盘服务器网卡延时高,无盘网卡优化-解决秒卡,速度慢,速度不稳定问题
  7. sql date_format用法
  8. 白领巧学燕子飞可治颈椎疼
  9. 几种常见代码管理工具比较(2009)
  10. ubuntu下安装Google谷歌浏览器(64位系统)
  11. 人力资源管理专业知识与实务(初级)【11】
  12. 金额的每三位一个逗号的正则解法
  13. 百度云加速 ajax,百度云加速或360网站卫士关于WordPress评论缓存的巧妙设置
  14. 互联网创业,也许一开始就不是草根的天下
  15. php zip提示未安装包,请大神赐教:centos7安装zip扩展成功,但是打印phpinfo未加载成功...
  16. Tradeoff 是一种针对目标选择有效的路径的思维方式
  17. 程序分析过程中遇到疑难问题解决办法
  18. 腾讯云 - OCR-身份证识别
  19. Sanic框架下部署Pytorch模型
  20. 2017中国(上海)国际物联网大会在上海隆重召开

热门文章

  1. 大庆师范学院计算机系徐媛老师,大庆师范学院课程表(未添加英语课).xls
  2. mysql hypot_mysqli_stmt_prepare
  3. oracle 执行sql参数混乱,乱用_allow_resetlogs_corruption参数导致悲剧
  4. php登录框注入,分享一个php的防火墙,拦截SQL注入和xss
  5. 计算机一级access考试题库,‎App Store 上的“计算机二级access-全国计算机等级考试题库”...
  6. 五十七、Servlet工程和Tomcat
  7. 二十三、前端必学Node.js入门
  8. cmd批处理命令与变量(下)
  9. 计算机英语课程 ppt,Unit 3_计算机专业英语_doc_大学课件预览_高等教育资讯网
  10. matlab中ismember_MATLAB 代码格式化简易版