多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開(kāi)源 > 綜合技術(shù) > Android中ClassLoader源碼解析之真的是你認(rèn)為的ClassLoader

Android中ClassLoader源碼解析之真的是你認(rèn)為的ClassLoader

來(lái)源:程序員人生   發(fā)布時(shí)間:2016-11-05 08:17:24 閱讀次數(shù):2877次

1.前言

首先,瀏覽本文章之前,需要了解java中的ClassLoader的基本原理,包括java中的3級(jí)ClassLoader機(jī)制和ClassLoader的拜托機(jī)制,否則下面的內(nèi)容會(huì)不知道在講甚么。雖然Android中的ClassLoader也是遵守其拜托機(jī)制,但是他沒(méi)有遵守java的3級(jí)ClassLoader機(jī)制,而是自己造了1個(gè),修改了java系統(tǒng)的代碼,如果將二者混淆的話,在Android中使用ClassLoader的時(shí)候,你可能會(huì)遇到想不通的問(wèn)題。(由于作者曾就踩過(guò)坑。。。。。。)

首先,我們看下面1段代碼,在Application的onCreate()中添加以下代碼:
PathClassLoader classLoader = (PathClassLoader) getApplicationContext().getClassLoader(); Log.d("mytest", "classLoader : " + classLoader + "\n" + "parent : " + classLoader.getParent() + "\n" + "grandParent : " + classLoader.getParent().getParent() + "\n" + "system classloader : " + ClassLoader.getSystemClassLoader() + "\n" + "system parent : " + ClassLoader.getSystemClassLoader().getParent());

代碼的履行結(jié)果,打印內(nèi)容以下:
classLoader : dalvik.system.PathClassLoader[dexPath=/data/app/com.gavin.demo2application⑴.apk,libraryPath=/data/app-lib/com.gavin.demo2application⑴] parent : java.lang.BootClassLoader@41099128 grandParent : null system classloader : dalvik.system.PathClassLoader[dexPath=.,libraryPath=null] system parent : java.lang.BootClassLoader@41099128

看到上面的打印的內(nèi)容,我們了解到在Android的項(xiàng)目中使用的ClassLoader是其自定義的PathClassLoader,最重要的1點(diǎn)是打印的dexPath,這個(gè)決定了在項(xiàng)目代碼中要加載的類(lèi)的位置(后面詳細(xì)講授)。
第2點(diǎn)奇怪的地方就是它的parent是 BootClassLoader,這個(gè)又是甚么東西,后面詳細(xì)講授。
第3點(diǎn)調(diào)用ClassLoader.getSystemClassLoader()返回的是PathClassLoader,并且dexPath為. ,我們了解的java中的ClassLoader.getSystemClassLoader()返回的是加載classpath里面的class的ClassLoader,也就是java中的第3級(jí)ClassLoader,它是調(diào)用sun.misc.Launcher的getClassLoader()方法獲得的,詳細(xì)解析請(qǐng)自行查閱。
第4點(diǎn)它的parent也是BootClassLoader,看來(lái)這個(gè)必須要分析1下下,畢竟出鏡率這么高。

2.Context.getClassLoader()返回的是PathClassLoader

首先,Android中可使用的CLassLoader有PathClassLoader和DexClassLoader,PathClassLoader只能加載dex文件,我們安裝apk以后會(huì)在/data/dalvik-cache目錄下生產(chǎn)1個(gè)名為data@app@com.hujiang.xxx⑴.apk@classes.dex的 ODEX 文件,而PathClassLoader要加載apk的時(shí)候會(huì)到這個(gè)文件夾下找對(duì)應(yīng)的dex文件。(ODEX文件就是經(jīng)過(guò)優(yōu)化的dex文件,詳細(xì)自行查閱),同時(shí)也是我們自己編寫(xiě)的項(xiàng)目中使用的ClassLoader。而DexClassLoader可以加載apk,dex,jar文件,就是被用來(lái)實(shí)現(xiàn)動(dòng)態(tài)加載機(jī)制,加載1個(gè)外部的apk文件,實(shí)現(xiàn)完全解耦的模塊式開(kāi)發(fā),現(xiàn)在的開(kāi)源框架有DL(使用代理的方式,其實(shí)加載的不是插件中的類(lèi))和DroinPlugin(hook掉AMS和PMS實(shí)現(xiàn));現(xiàn)在比較火的熱修改也是和其有關(guān)系,比如AndFix(它是在運(yùn)行時(shí)將java方法修改成native方法,然后修改調(diào)用這個(gè)方法的指針,指向修復(fù)的方法),nuwa(也就是qq空間實(shí)現(xiàn)基于dex分包,修改CLassLoader中的dexElements中的dex順序?qū)崿F(xiàn))和最新的美團(tuán)的Robust(基于Android Studio的instance run的原理,為每一個(gè)類(lèi)創(chuàng)建代理類(lèi))。
關(guān)于上面所提的動(dòng)態(tài)加載框架,熱修復(fù)框架等等都會(huì)在后續(xù)的文章中進(jìn)行分析。

上面說(shuō)了那末多,現(xiàn)在開(kāi)始分析PathClassLoader和DexCLassLoader的源碼實(shí)現(xiàn),他們都是繼承BaseDexClassLoader,所有的實(shí)現(xiàn)都瘋轉(zhuǎn)在了這個(gè)類(lèi)里面,先來(lái)看看PathClassLoader和DexClassLoader的源碼.
PathClassLoader的代碼:
public class PathClassLoader extends BaseDexClassLoader { 37 public PathClassLoader(String dexPath, ClassLoader parent) { 38 super(dexPath, null, null, parent); 39 } 40 63 public PathClassLoader(String dexPath, String libraryPath, 64 ClassLoader parent) { 65 super(dexPath, null, libraryPath, parent); 66 } 67} 68

DexClassLoader源碼以下:
public class DexClassLoader extends BaseDexClassLoader { 55 public DexClassLoader(String dexPath, String optimizedDirectory, 56 String libraryPath, ClassLoader parent) { 57 super(dexPath, new File(optimizedDirectory), libraryPath, parent); 58 } 59}
可以看出他們僅僅是重寫(xiě)了構(gòu)造函數(shù),所以所有的實(shí)現(xiàn)都是在BaseDexClassLoader里面。
參數(shù):dexPath:要加載的apk或jar文件的路徑,optimizedDirectory:從apk中解析出dex文件存儲(chǔ)的路徑,libraryPath:apk文件中類(lèi)要使用的c/c++代碼,parent:父裝載器,也就是真正loadclass的裝載器。

下面重點(diǎn)來(lái)了,分析BaseDexClassLoader的源碼,首先構(gòu)造函數(shù)以下:
public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { super(parent); this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); }
super()走的是ClassLoader的設(shè)置parent的ClassLoader的方式,重點(diǎn)是下面的PathList,他存儲(chǔ)的是dex的集合,由于apk是可以dex分包,它里面含有1個(gè)DexElement的集合,每個(gè)Element就對(duì)應(yīng)1個(gè)dex文件。

DexPathList的構(gòu)造函數(shù)的核心代碼以下:
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
makeDexElements()就是解析dex文件成對(duì)應(yīng)的DexElement,代碼以下:
private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) { ArrayList<Element> elements = new ArrayList<Element>(); 209 /* 210 * Open all files and load the (direct or contained) dex files 211 * up front. 212 */ 213 for (File file : files) {//遍歷所有的dex文件 214 File zip = null; 215 DexFile dex = null;//這是核心的類(lèi),處理將dex文件轉(zhuǎn)化成對(duì)應(yīng)的DexFile對(duì)象 216 String name = file.getName(); 217 218 if (name.endsWith(DEX_SUFFIX)) {//.dex結(jié)尾(針對(duì)PathClassLoader處理) 219 // Raw dex file (not inside a zip/jar). 220 try { 221 dex = loadDexFile(file, optimizedDirectory);//核心方法,內(nèi)部是調(diào)用的DexFile的loadDex()方法 222 } catch (IOException ex) { 223 System.logE("Unable to load dex file: " + file, ex); 224 } 225 } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX) 226 || name.endsWith(ZIP_SUFFIX)) {//.dex .jar .apk 結(jié)尾 (針對(duì)DexClassLoader處理) 227 zip = file; 228 229 try { 230 dex = loadDexFile(file, optimizedDirectory); 231 } catch (IOException suppressed) { 232 /* 233 * IOException might get thrown "legitimately" by the DexFile constructor if the 234 * zip file turns out to be resource-only (that is, no classes.dex file in it). 235 * Let dex == null and hang on to the exception to add to the tea-leaves for 236 * when findClass returns null. 237 */ 238 suppressedExceptions.add(suppressed); 239 } 240 } else if (file.isDirectory()) { 241 // We support directories for looking up resources. 242 // This is only useful for running libcore tests. 243 elements.add(new Element(file, true, null, null));//創(chuàng)建Element對(duì)象 244 } else { 245 System.logW("Unknown file type for: " + file); 246 } 247 248 if ((zip != null) || (dex != null)) { 249 elements.add(new Element(file, false, zip, dex)); 250 } 251 } 252 253 return elements.toArray(new Element[elements.size()]); 254 }
上面的代碼是遍歷所有的dex文件,然后調(diào)用的DexFile的loadDex()方法,內(nèi)部就是創(chuàng)建1個(gè)DexFile對(duì)象,這個(gè)構(gòu)造函數(shù)中會(huì)調(diào)用openDexFile()解析dex文件,這是1個(gè)native()方法,使用c代碼實(shí)現(xiàn)的,這里就不分析了,如果想要學(xué)習(xí),推薦1篇博客:DexClassLoader源碼解析
關(guān)于DexFile,AndFix就是直接使用DexFile直接加載的dex文件,而沒(méi)有使用DexClassLoader,有興趣可以查看AndFix的源碼。

3.Context的getClassLoader()為何返回是PathClassLoader,探索其中奧秘

我們都知道getApplicationContext()的返回的實(shí)現(xiàn)類(lèi)是ContextImpl,下面來(lái)看看ContextImpl的getClassLoader()的代碼實(shí)現(xiàn):
public ClassLoader getClassLoader() { return mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader(); }
我們看到如果mPackageInfo不為null,就調(diào)用它的getClassLoader()方法,否則調(diào)用ClassLoader.getSystemClassLoader(),這里我們看到了ClassLoader的getSystemClassLoader()方法,但是這里還不是重點(diǎn),重點(diǎn)是mPackageInfo這個(gè)對(duì)象,這是甚么呢,它是1個(gè)LoadedAPK對(duì)象。它又是甚么呢?
官方文檔說(shuō)明以下:
Local state maintained about a currently loaded .apk.
LoaderAPK對(duì)象是apk在內(nèi)存中的表示。通過(guò)這個(gè)LoaderApk對(duì)象可以拿到apk中代碼和資源,乃至里面Activity和Service等信息。
那末它又是哪里創(chuàng)建的,又是甚么時(shí)候創(chuàng)建的呢,如果你了解Activity的啟動(dòng)進(jìn)程,你就明白了(如果想了解,推薦老羅的文章),這里就不贅述了。在ActivityThread里面有1個(gè)mPackages的map類(lèi)型的成員變量,根據(jù)鍵值(packageName)存儲(chǔ)對(duì)應(yīng)的LoadedApk對(duì)象。啟動(dòng)Activity的時(shí)候要調(diào)用LoadedApk的getClassLoader(),來(lái)加載對(duì)應(yīng)的Activity class文件,然后通過(guò)反射創(chuàng)建這個(gè)activity的實(shí)例;那末獲得這個(gè)對(duì)象,會(huì)先去mPackages中去查找有無(wú)緩存,如果沒(méi)有就創(chuàng)建1個(gè)新的LoadedAPK對(duì)象。
下面代碼截取自ActivityThread中啟動(dòng)Activity的進(jìn)程中創(chuàng)建LoadedApk的代碼:
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { return getPackageInfo(ai, compatInfo, null, false, true, false); }
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode, boolean registerPackage) { // 獲得userid信息 final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); synchronized (mResourcesManager) { // 嘗試獲得緩存信息 WeakReference<LoadedApk> ref; if (differentUser) { // Caching not supported across users ref = null; } else if (includeCode) { ref = mPackages.get(aInfo.packageName); } else { ref = mResourcePackages.get(aInfo.packageName); } LoadedApk packageInfo = ref != null ? ref.get() : null; if (packageInfo == null || (packageInfo.mResources != null && !packageInfo.mResources.getAssets().isUpToDate())) { // 緩存沒(méi)有命中,直接new packageInfo = new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); // 省略。。更新緩存 return packageInfo; } }
下面看上面使用到的LoadedApk的構(gòu)造函數(shù),其實(shí)LoadedApk還有1個(gè)構(gòu)造函數(shù),在ContextImpl創(chuàng)建自己的實(shí)例的同時(shí)創(chuàng)建其LoadedApk的成員變量的時(shí)候使用了。
108 public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo, 109 CompatibilityInfo compatInfo, 110 ActivityThread mainThread, ClassLoader baseLoader, 111 boolean securityViolation, boolean includeCode) { <span style="white-space:pre"> </span> .......(省略) 126 127 if (mAppDir == null) { 128 if (ActivityThread.mSystemContext == null) {//這個(gè)context很重要,1個(gè)ActivityThread只有這1個(gè),是靜態(tài)全局的 129 ActivityThread.mSystemContext = 130 ContextImpl.createSystemContext(mainThread); 131 ActivityThread.mSystemContext.getResources().updateConfiguration( 132 mainThread.getConfiguration(), 133 mainThread.getDisplayMetricsLocked(compatInfo, false), 134 compatInfo); 135 //Slog.i(TAG, "Created system resources " 136 // + mSystemContext.getResources() + ": " 137 // + mSystemContext.getResources().getConfiguration()); 138 } 139 mClassLoader = ActivityThread.mSystemContext.getClassLoader();//這個(gè)ClassLoader就是最后返回的那個(gè)ClassLoader 140 mResources = ActivityThread.mSystemContext.getResources(); 141 } 142 } 143
看到這里,我們只要終究這個(gè)CLassLoader的來(lái)源是從Context那里后去的,也就是ActivityThread的mSystemContext里面的ClassLoader,我們來(lái)看1下這個(gè)mSystemContext的創(chuàng)建進(jìn)程,代碼以下:
1458 static ContextImpl createSystemContext(ActivityThread mainThread) { 1459 ContextImpl context = new ContextImpl(); 1460 context.init(Resources.getSystem(), mainThread);//這個(gè)init操作也沒(méi)有創(chuàng)建他里面LoadedApk成員變量 1461 return context; 1462 }
所以終究調(diào)用的代碼,就是最開(kāi)始的ContextImpl的getClassLoader()方法,并且mPackageInfo(LoadedAPK對(duì)象)為null,所以終究調(diào)用的是ClassLoader.getSystemClassLoader(),所以終究結(jié)論就是系統(tǒng)ClassLoader是通過(guò)ClassLoader.getSystemClassLoader()創(chuàng)建。

4.揭開(kāi)ClassLoader.getSystemClassLoader()在Android中的神秘面紗


其代碼以下:
public static ClassLoader getSystemClassLoader() { return SystemClassLoader.loader; }
static private class SystemClassLoader { public static ClassLoader loader = ClassLoader.createSystemClassLoader(); }
private static ClassLoader createSystemClassLoader() { String classPath = System.getProperty("java.class.path", "."); return new PathClassLoader(classPath, BootClassLoader.getInstance()); }
對(duì)最有1個(gè)核心方法createSystemClassLoader(),官方說(shuō)明以下:
Create the system class loader. Note this is NOT the bootstrap class loader (which is managed by the VM). We use a null value for the parent to indicate that the bootstrap loader is our parent.
創(chuàng)建系統(tǒng)的ClassLoader。注釋:這不是bootstrap ClassLoader(被虛擬機(jī)管理的ClassLoader)。我們使用null作為我們系統(tǒng)ClassLoader的parent來(lái)表明bootstrap就是我們的系統(tǒng)ClassLoader的parent。這里也就是充分辯明了Android系統(tǒng)不是使用的java原生的bootstrap來(lái)加載,而是使用自己的創(chuàng)建的套機(jī)制。取代bootstrap的是用null(bootstrap不是1個(gè)ClassLoader對(duì)象,所以他的子級(jí)ClassLoader調(diào)用getParent()返回的是null),而取代java中第2級(jí)的ClassLoader使用Android中創(chuàng)建的最基層的BootClassLoader(也就是上面的PathClassLoader的parent)。
這個(gè)BootClassLoader是單例的,所以全局只有1個(gè),我們也能夠得出,系統(tǒng)所有履行裝載類(lèi)的操作,都會(huì)履行到這個(gè)對(duì)象。代碼以下:
public BootClassLoader() { super(null, true);//他的parent為null(摹擬它的parent是bootstrap) }

還有我們看到上面PathClassLoader構(gòu)造函數(shù)中傳遞的第1個(gè)參數(shù)classPath,這個(gè)就是我們之前打印出來(lái)的dexPath。為何系統(tǒng)的ClassLoader.getSystemClassLoader()返回的PathClassLoader 的dexPath指向的是當(dāng)前apk的安裝路徑;但是我們自己創(chuàng)建的返回的PathClassLoader的dexPath卻是.  我們發(fā)現(xiàn)是由上面的System.getProperty("java.class.path",".");決定的,看到get操作,相對(duì)的也就會(huì)有set操作,那末這個(gè)值是甚么時(shí)候設(shè)置的呢。這我們就要追溯到Android framwork中System中的initSystemProperty()方法中,調(diào)用VMRuntime.getRuntime().classPath()的值,這個(gè)值應(yīng)當(dāng)就是當(dāng)前apk的安裝路徑。
代碼以下: 路徑:/libcore/luni/src/main/java/java/lang/System.java(4.0.4)
private static void initSystemProperties() { VMRuntime runtime = VMRuntime.getRuntime(); Properties p = new Properties(); String projectUrl = "http://www.android.com/"; String projectName = "The Android Project"; p.put("java.boot.class.path", runtime.bootClassPath()); p.put("java.class.path", runtime.classPath()); }
但是,為何我們自己調(diào)用,獲得這個(gè)值就是. 我的料想就是 系統(tǒng)創(chuàng)建完對(duì)應(yīng)的ClassLoader以后,就將這個(gè)值修改成了.(固然這僅僅是我個(gè)人的料想,希望了解的大神能幫我解答1下,由于我實(shí)在是找不到緣由,困惑好久了)。


5.總結(jié)

1.Android系統(tǒng)最頂級(jí)的ClassLoader是BootClassLoader(替換java中第2級(jí)的ext ClassLoader),而用來(lái)加載系統(tǒng)的類(lèi)是使用的以這兒BootClassLoader為parent的PathClassLoader(替換java中第3級(jí)加載classpath的ClassLoader)。

2.Android系統(tǒng)的PathClassLoader的dexPath(要加載類(lèi)的路徑),指向當(dāng)前apk安裝的路徑,然后使用DexFile來(lái)解析對(duì)應(yīng)的dex文件,裝載里面的class

3.DexClassLoader,主要用于加載外部插件,也就是可以直接加載1個(gè)apk文件,現(xiàn)在的插件化動(dòng)態(tài)加載機(jī)制,熱修復(fù)等都要使用到它的特性,固然直接使用里面的DexFile直接加載dex文件也是可以(AndFix就是這樣做的)。

4.自己遇到的坑:我之前在學(xué)習(xí)動(dòng)態(tài)化的時(shí)候,看完原理以后,自己創(chuàng)建1個(gè)DexClassLoader ,它的parent我設(shè)置的是系統(tǒng)PathClassLoader(也就是context.getClassLoader()),但是load外部apk中的類(lèi)的時(shí)候,報(bào)錯(cuò):當(dāng)前apk的安裝路徑下(也就是這個(gè)apk文件對(duì)應(yīng)生成的ODEX文件)找不到對(duì)應(yīng)的類(lèi),也就是ClassNotFoundException,但是自己不明白為何我用DexClassLoader加載的外部apk,但是它卻到系統(tǒng)中去尋覓。現(xiàn)在明白了由于我傳遞的是PathClassLoader的dexPath是指向系統(tǒng)的apk的路徑的,自然會(huì)到那里去找。但是如果傳遞的是ClassLoader.getSystemClassLoader(),由于dexPath是. ,是Directory,就不會(huì)創(chuàng)建DexFile,也就不會(huì)走DexFile的loadDexFile()方法,而是直接調(diào)用它的parent也就是BootClassLoader的findClass()來(lái)裝載當(dāng)前類(lèi)。

文章終究寫(xiě)完了,鄙人愚鈍,水平有限,文章難免有錯(cuò),歡迎指出。










生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 爱插综合网 | 国产视频自拍一区 | 国产人做人爱免费视频 | 欧美午夜理伦三级理论三级 | 欧美最猛性xxxx高清 | 欧美日韩亚洲国产一区二区综合 | 亚洲资源站 | 岛国福利片| 手机看片亚洲 | 国产观看精品一区二区三区 | 国产亚洲精品久久久久久 | 刺激第一页720lu久久 | 亚洲精品一二三四区 | 嗯啊羞羞视频在线观看动漫 | 欧美一级α片 | 女性一级全黄生活片 | 毛片网站网址 | 狂野欧美性猛交xxxx巴西 | 天天干夜夜骑 | 日本h片无遮挡在线观看 | 自由成熟的性色视频免费观看 | 亚洲精品成人一区二区aⅴ 亚洲精品成人在线 | 五月婷婷六月丁香综合 | 国产女人18一级毛片视频 | 另类校园春色 | 亚洲乱码一区二区三区在线观看 | 99伊人精品 | 亚洲综合激情另类专区 | 亚洲另类春色 | 国产免费高清视频在线观看不卡 | 亚洲成人一区 | 欧美精品网站 | 亚洲人成免费 | 久久国产综合 | 亚洲综合久久久 | 免费在线观看一级毛片 | 欧美一区二区三区视频在线观看 | 欧美色欧| 久久精品免费观看 | 女人18特级一级毛片免费视频 | 精品综合 |