转载自:最新内容及最清晰格式请见 http://www.trinea.cn/android/java-loader-common-class/java
本文主要介绍 ClassLoader 的基础知识,ClassLoader 如何动态加载 Jar,ClassLoader 隔离问题及如何加载不一样 Jar 中的公共类。android
本文工程开源地址见:Java Dynamic Load Jar@Github,Clone 之后直接以 Java Application去运行 java-dynamic-loader-host 工程便可。git
其实本文只是 Android 插件化的一个引子,作过 Android 插件化的同窗,能够试试对于 Android Support 包中的 FragmentActivity 和 ActionBarActivity 怎么像通常的 Activity 同样被代理,挺有意思。github
1. ClassLoader 的基础知识
不管是 JVM 仍是 Dalvik 都是经过 ClassLoader 去加载所须要的类,而 ClassLoader 加载类的方式常称为双亲委托,ClassLoader.java 具体代码以下:函数
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(className); if (clazz == null) { try { clazz = parent.loadClass(className, false); } catch (ClassNotFoundException e) { // Don't want to see this. } if (clazz == null) { clazz = findClass(className); } } return clazz; }
从上面加载类的顺序中咱们能够知道,loadClass 会先看这个类是否是已经被 loaded 过,没有的话则去他的 parent 去找,如此递归,称之为双亲委托。测试
2. 动态加载 Jar
Java 中动态加载 Jar 比较简单,以下:this
URL[] urls = new URL[] {new URL("file:libs/jar1.jar")}; URLClassLoader loader = new URLClassLoader(urls, parentLoader);
表示加载 libs 下面的 jar1.jar,其中 parentLoader 就是上面1中的 parent,能够为当前的 ClassLoader。url
3. ClassLoader 隔离问题
你们以为一个运行程序中有没有可能同时存在两个包名和类名彻底一致的类?
JVM 及 Dalvik 对类惟一的识别是 ClassLoader id + PackageName + ClassName,因此一个运行程序中是有可能存在两个包名和类名彻底一致的类的。而且若是这两个”类”不是由一个 ClassLoader 加载,是没法将一个类的示例强转为另一个类的,这就是 ClassLoader 隔离。 如 Android 中碰到以下异常插件
android.support.v4.view.ViewPager can not be cast to android.support.v4.view.ViewPager
当碰到这种问题时能够经过 instance.getClass().getClassLoader(); 获得 ClassLoader,看 ClassLoader 是否同样。代理
4. 加载不一样 Jar 包中公共类
如今 Host 工程包含了 common.jar, jar1.jar, jar2.jar,而且 jar1.jar 和 jar2.jar 都包含了 common.jar,咱们经过 ClassLoader 将 jar1, jar2 动态加载进来,这样在 Host 中实际是存在三份 common.jar,以下图:
咱们怎么保证 common.jar 只有一份而不会形成上面3中提到的 ClassLoader 隔离的问题呢,其实很简单,有三种方式:
第一种:咱们只要让加载 jar1 和 jar2 的 ClassLoader 的 parent 为同一个 ClassLoader,而且该 ClassLoader 加载过 common.jar,经过上面 1 中咱们知道根据双亲委托,最后都会首先被 parentClassLoader加载。
第二种:咱们重写 jar1 和 jar2 的 ClassLoader,在 loadClass 函数中咱们先去某个含有 common.jar 的 ClassLoader 中 load 便可,其实就是把上面的 parentClassLoader 换掉了而已。
第三种:在生成 jar1 和 jar2 时把 common.jar 去掉,只保留 host 中一份,以 host ClassLoader 为 parentClassLoader 便可。
具体可见代码:JarClassLoader
你们测试后会发现对于 Java 是正常的,而方式一和方式二对于 Android 却失败,具体缘由下次再说吧