日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

一. 概述

Android從5.0開始就采用art虛擬機, 該虛擬機有些類似JAVA虛擬機, 程序運行過程也需要通過ClassLoader 將目標(biāo)類加載到內(nèi)存.

傳統(tǒng)Jvm主要是通過讀取class字節(jié)碼來加載, 而art則是從dex字節(jié)碼來讀取. 這是一種更為優(yōu)化的方案, 可以將多個.class文件合并成一個classes.dex文件. 下面直接來看看ClassLoader的關(guān)系。

二. 五種類構(gòu)造器

接下來依次看看PathClassLoader,DexClassLoader,BaseDexClassLoader,BootClassLoader,ClassLoader這5個類加載器。

PathClassLoader和DexClassLoader,它們都繼承自BaseDexClassLoader,這兩個類有什么區(qū)別呢?其實看一下它們的源碼注釋就一目了然了。因為代碼很少,約等于沒有,這里直接貼出它們的源碼,其實主要是注釋:

2.1 PathClassLoader

淺談Android類加載器

 


淺談Android類加載器

 

由注釋看可以發(fā)現(xiàn)PathClassLoader被用來加載本地文件系統(tǒng)上的文件或目錄,但不能從網(wǎng)絡(luò)上加載,關(guān)鍵是它被用來加載系統(tǒng)類和我們的應(yīng)用程序,這也是為什么它的兩個構(gòu)造函數(shù)中調(diào)用父類構(gòu)造器的時候第二個參數(shù)傳null,具體的參數(shù)意義請看接下來DexClassLoader的注釋。

2.2 DexClassLoader

淺談Android類加載器

 

DexClassLoader用來加載jar、apk,其實還包括zip文件或者直接加載dex文件,它可以被用來執(zhí)行未安裝的代碼或者未被應(yīng)用加載過的代碼。這里也寫出了它需要的四個參數(shù)的意思

  1. dexPath:需要被加載的文件地址,可以多個,用File.pathSeparator分割
  2. optimizedDirectory:dex文件被加載后會被編譯器優(yōu)化,優(yōu)化之后的dex存放路徑,不可以為null。注意,注釋中也提到需要一個應(yīng)用私有的可寫的一個路徑,以防止應(yīng)用被注入攻擊,并且給出了例子 File dexOutputDir = context.getDir("dex", 0);
  3. libraryPath:包含libraries的目錄列表,plugin中有so文件,需要將so拷貝到sd卡上,然后把so所在的目錄當(dāng)參數(shù)傳入,同樣用File.pathSeparator分割,如果沒有則傳null就行了,會自動加上系統(tǒng)so庫的存放目錄
  4. parent:父類構(gòu)造器

這里著重看一下第二個參數(shù),之前說過PathClassLoader中調(diào)用父類構(gòu)造器的時候這個參數(shù)穿了null,因為加載App應(yīng)用的時候我們的apk已經(jīng)被安裝到本地文件系統(tǒng)上了,其內(nèi)部的dex已經(jīng)被提取并且執(zhí)行過優(yōu)化了,優(yōu)化之后放在系統(tǒng)目錄/data/dalvik-cache下。

2.3 BaseDexClassLoader

淺談Android類加載器

 

BaseDexClassLoader構(gòu)造函數(shù), 有一個非常重要的過程, 那就是初始化DexPathList對象.

另外該構(gòu)造函數(shù)的參數(shù)說明:

  • dexPath: 包含目標(biāo)類或資源的apk/jar列表;當(dāng)有多個路徑則采用:分割;
  • optimizedDirectory: 優(yōu)化后dex文件存在的目錄, 可以為null;
  • libraryPath: native庫所在路徑列表;當(dāng)有多個路徑則采用:分割;
  • ClassLoader:父類的類加載器.

2.4 BootClassLoader

淺談Android類加載器

 

2.5 ClassLoader

淺談Android類加載器

 

再來看看SystemClassLoader,這里的getSystemClassLoader()返回的是PathClassLoader類。

淺談Android類加載器

 

3. 類加載實例

首先看一段如何使用類加載器加載的調(diào)用代碼:

1 try {
2 File file = view.getActivity().getDir("dex",0);
3 String optimizedDirectory = file.getAbsolutePath();
4 DexClassLoader loader = new DexClassLoader("需要被加載的dex文件所在的路徑",optimizedDirectory,null,context.getClassLoader());
5 loader.loadClass("需要加載的類的完全限定名");
6 } catch (ClassNotFoundException e) {
7 e.printStackTrace();
8 }

這里我們就用了自定義了一個DexClassLoaderLoader,并且調(diào)用了它的loadClass方法,這樣一個需要被使用的類就被我們加載進來了,接下去就可以正常使用這個類了,具體怎么使用我就不多說了,我們還是來研究研究這個類是怎么被加載進來的吧~

可以看到new DexClassLoader的時候我們用了4個參數(shù),參數(shù)意義上面已經(jīng)講過了,從上面的源碼中可以看到DexClassLoader的構(gòu)造器中直接調(diào)用了父類的構(gòu)造器,只是將optimizedDirectory路徑封裝成一個File,具體這些參數(shù)是如何被使用的呢,我們往下看。

BaseDexClassLoader類的構(gòu)造器

淺談Android類加載器

 

首先也是調(diào)用了父類的構(gòu)造器,但這里只將parent傳給父類,即ClassLoader,ClassLoader中做的也很很簡單,它內(nèi)部有個parent屬性,正好保存?zhèn)鬟M來的參數(shù)parent,這里可以稍微看一下第二個參數(shù)的注釋,最后一句說到可以為null,而是否為null又剛好是PathClassLoader和DexClassLoader的區(qū)別,那是否為null最終又意味著什么呢?

淺談Android類加載器

 

接下來BaseDexClassLoader給originalPath 和 pathList賦了值,originalPath就是我們傳進入的dex文件路徑,pathList 是一個new 出來的DexPathList對象。

淺談Android類加載器

 

別的先不說,先看注釋。第四個參數(shù)中說到如果optimizedDirectory 為null則使用系統(tǒng)默認(rèn)路徑代替,這個默認(rèn)路徑也就是/data/dalvik-cache/目錄,這個一般情況下是沒有權(quán)限去訪問的,所以這也就解釋了為什么我們只能用DexClassLoader去加載類而不用PathClassLoader。

然后接著看代碼,顯然,前面三個if判斷都是用來驗證參數(shù)的合法性的,之后同樣只是做了三個賦值操作,第一個就不說了,保存了實例化DexPathList的classloader,第二個參數(shù)的聲明是一個Element數(shù)組,第三個參數(shù)是lib庫的目錄文件數(shù)組。

淺談Android類加載器

 

看它們之前先看看幾個split小函數(shù):

淺談Android類加載器

 

這兩個顧名思義就是拿來分割dexPath和libPath,它們內(nèi)部都調(diào)用了splitPaths方法,只是三個參數(shù)不一樣,其中splitLibraryPath方法中調(diào)用splitPaths時的第二個參數(shù)仿佛又透露了什么信息,沒錯,之前介紹DexClassLoader參數(shù)中的libraryPath的時候說過,會加上系統(tǒng)so庫的存放目錄,就是在這個時候添加上去的。

淺談Android類加載器

 

什么啊,原來這個方法也沒做什么事啊,只是把參數(shù)path1和參數(shù)path2又分別調(diào)用了下splitAndAdd方法,但是這里創(chuàng)建了一個ArrayList,而且調(diào)用splitAndAdd方法的時候都當(dāng)參數(shù)傳入了,并且最終返回了這個list,所以我們大膽猜測下,path1和path2最后被分割后的值都存放在了list中返回,看下是不是這么一回事吧:

淺談Android類加載器

 

果然,跟我們猜的一樣,只是又加上了文件是否存在以及是否可讀的驗證,然后根據(jù)參數(shù)wantDirectories判斷是否文件類型是被需要的類型,最終加入list。現(xiàn)在我們回過頭去看看splitDexPath方法和splitLibraryPath方法,是不是一目了然了。

再往上看DexPathList的構(gòu)造器,nativeLibraryDirectories的最終值也已經(jīng)知道了,就差dexElements了,makeDexElements方法的兩個參數(shù)我們也已經(jīng)知道了,那我們就看看makeDexElements都干了些什么吧。

淺談Android類加載器

 


淺談Android類加載器

 

方法也不長,我們一段段看下去。首先創(chuàng)建了一個elememt 列表,然后遍歷由dexpath分割得來的文件列表,其實一般使用場景下也就一個文件。循環(huán)里面針對每個file 聲明一個zipfile和一個dexfile,判斷file的文件后綴名,如果是".dex"則使用loadDexFile方法給dex賦值,如果是“.apk”,“.jar”,“.zip”文件的則把file包裝成zipfile賦值給zip,然后同樣是用loadDexFile方法給dex賦值,如果是其他情況則不做處理,打印日志說明文件類型不支持,而且下一個if判斷中由于zip和dex都未曾賦值,所以也不會添加到elements列表中去。注意下:這里所謂的文件類型僅僅是指文件的后綴名而已,并不是文件的實際類型,比如我們將.zip文件后綴改成.txt,那么就不支持這個文件了。而且我們可以看到對于dexpath目前只支持“.dex”、“.jar”、“.apk”、“.zip”這四種類型。

現(xiàn)在還剩下兩個東西可能還不太明確,一個是什么是DexFile以及這里的loadDexFile方法是如何創(chuàng)建dexfile實例的,另一個是什么是Elememt,看了下Element源碼,哈哈,so easy,就是一個簡單的數(shù)據(jù)結(jié)構(gòu)體加一個方法,所以我們就先簡單把它當(dāng)做一個存儲了file,zip,dex三個字段的一個實體類。那么就剩下DexFile了。

淺談Android類加載器

 

很簡潔,如果optimizedDirectory == null則直接new 一個DexFile,否則就使用DexFile.loadDex來創(chuàng)建一個DexFile實例。

淺談Android類加載器

 

這個方法獲取被加載的dexpath的文件名,如果不是“.dex”結(jié)尾的就改成“.dex”結(jié)尾,然后用optimizedDirectory和新的文件名構(gòu)造一個File并返回該File的路徑,所以DexFile.loadDex方法的第二個參數(shù)其實是dexpath文件對應(yīng)的優(yōu)化文件的輸出路徑。比如我要加載一個dexpath為“sdcard/coder_yu/plugin.apk”,optimizedDirectory 為使用范例中的目錄的話,那么最終優(yōu)化后的輸出路徑為/data/user/0/com.coder_yu.test/app_dex/plugin.dex,具體的目錄在不同機型不同rom下有可能會不一樣。

是時候看看DexFile了。在上面的loadDexFile方法中我們看到optimizedDirectory參數(shù)為null的時候直接返回new DexFile(file)了,否則返回 DexFile.loadDex(file.getPath(), optimizedPath, 0),但其實他們最終都是使用了相同方法去加載dexpath文件,因為DexFile.loadDex方法內(nèi)部也是直接調(diào)用的了DexFile的構(gòu)造器,以下:

淺談Android類加載器

 

然后看看DexFile的構(gòu)造器吧

淺談Android類加載器

 

可以看到直接new DexFile(file)和DexFile.loadDex(file.getPath(), optimizedPath, 0)最終都是調(diào)用了openDexFile(sourceName, outputName, flags)方法,只是直接new的方式optimizedPath參數(shù)為null,這樣openDexFile方法會默認(rèn)使用 /data/dalvik-cache目錄作為優(yōu)化后的輸出目錄,第二個構(gòu)造器的注釋中寫的很明白了。mCookie是一個int值,保存了openDexFile方法的返回值,openDexFile方法是一個native方法,我們就不深入了。

四. 總結(jié)

幾種類加載器:

  • PathClassLoader: 主要用于系統(tǒng)和app的類加載器,其中optimizedDirectory為null, 采用默認(rèn)目錄/data/dalvik-cache/
  • DexClassLoader: 可以從包含classes.dex的jar或者apk中,加載類的類加載器, 可用于執(zhí)行動態(tài)加載,但必須是app私有可寫目錄來緩存odex文件. 能夠加載系統(tǒng)沒有安裝的apk或者jar文件, 因此很多插件化方案都是采用DexClassLoader;
  • BaseDexClassLoader: 比較基礎(chǔ)的類加載器, PathClassLoader和DexClassLoader都只是在構(gòu)造函數(shù)上對其簡單封裝而已.
  • BootClassLoader: 作為父類的類構(gòu)造器。

熱修復(fù)核心邏輯:在DexPathList.findClass()過程,一個Classloader可以包含多個dex文件,每個dex文件被封裝到一個Element對象,這些Element對象排列成有序的數(shù)組dexElements。當(dāng)查找某個類時,會遍歷所有的dex文件,如果找到則直接返回,不再繼續(xù)遍歷dexElements。也就是說當(dāng)兩個類不同的dex中出現(xiàn),會優(yōu)先處理排在前面的dex文件,這便是熱修復(fù)的核心精髓,將需要修復(fù)的類所打包的dex文件插入到dexElements前面。

類加載過程常見的ClassNotFound原因:

  • ABI異常:常見在系統(tǒng)APP,為了減小system分區(qū)大小會將apk源文件中的classes.dex文件移除,對于既然可運行在64位又可運行在32位模式的應(yīng)用,當(dāng)被強制設(shè)置32位時,openDexFileNative在查找不到oat文件時會運行在解釋模式,而classes.dex文件不再則出現(xiàn)ClassNotFound異常。
  • MultiDex處理不當(dāng),由于每個Dex文件中方法個數(shù)不能超過65536,引入MultiDex機制。dex2oat會自動查找Apk文件中的classes.dex,classes2.dex,…classesN.dex等文件,編譯到/data/dalvik-cache下生成oat文件。這里需要文件名跟classesN.dex格式,并且一定要與classes.dex一起放置在第一級目錄,有些APP不按照要求來,導(dǎo)致ClassNotFound異常。

好啦,文章寫到這里就結(jié)束了,如果你覺得文章寫得不錯就給個贊唄?如果你覺得那里值得改進的,請給我留言。一定會認(rèn)真查詢,修正不足。謝謝。

分享到:
標(biāo)簽:Android
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達(dá)人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定