轉載自:最新內容及最清晰格式請見http://www.trinea.cn/android/java-loader-common-class/
本文主要介紹 ClassLoader 的基礎知識,ClassLoader 如何動態加載 Jar,ClassLoader 隔離問題及如何加載不同 Jar 中的公共類。
本文工程開源地址見:Java Dynamic Load Jar@Github,Clone 以后直接以 Java application去運行 java-dynamic-loader-host 工程即可。
其實本文只是 Android 插件化的一個引子,做過 Android 插件化的同學,可以試試對于 Android Support 包中的 FragmentActivity 和 ActionBarActivity 怎么像一般的 Activity 一樣被代理,挺有意思。
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. 動態加載 JarJava 中動態加載 Jar 比較簡單,如下:
URL[] urls = new URL[] {new URL("file:libs/jar1.jar")};URLClassLoader loader = new URLClassLoader(urls, parentLoader);
表示加載 libs 下面的 jar1.jar,其中 parentLoader 就是上面1中的 parent,可以為當前的 ClassLoader。
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 卻失敗,具體原因下次再說吧
新聞熱點
疑難解答