轉載自:最新內容及最清晰格式請見 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 卻失敗,具體緣由下次再說吧