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

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

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

一. 概述

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

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

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

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

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

2.1 PathClassLoader

淺談Android類加載器

 


淺談Android類加載器

 

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

2.2 DexClassLoader

淺談Android類加載器

 

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

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

這里著重看一下第二個(gè)參數(shù),之前說過PathClassLoader中調(diào)用父類構(gòu)造器的時(shí)候這個(gè)參數(shù)穿了null,因?yàn)榧虞dApp應(yīng)用的時(shí)候我們的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ù), 有一個(gè)非常重要的過程, 那就是初始化DexPathList對(duì)象.

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

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

2.4 BootClassLoader

淺談Android類加載器

 

2.5 ClassLoader

淺談Android類加載器

 

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

淺談Android類加載器

 

3. 類加載實(shí)例

首先看一段如何使用類加載器加載的調(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 }

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

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

BaseDexClassLoader類的構(gòu)造器

淺談Android類加載器

 

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

淺談Android類加載器

 

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

淺談Android類加載器

 

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

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

淺談Android類加載器

 

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

淺談Android類加載器

 

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

淺談Android類加載器

 

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

淺談Android類加載器

 

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

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

淺談Android類加載器

 


淺談Android類加載器

 

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

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

淺談Android類加載器

 

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

淺談Android類加載器

 

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

是時(shí)候看看DexFile了。在上面的loadDexFile方法中我們看到optimizedDirectory參數(shù)為null的時(shí)候直接返回new DexFile(file)了,否則返回 DexFile.loadDex(file.getPath(), optimizedPath, 0),但其實(shí)他們最終都是使用了相同方法去加載dexpath文件,因?yàn)镈exFile.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方法會(huì)默認(rèn)使用 /data/dalvik-cache目錄作為優(yōu)化后的輸出目錄,第二個(gè)構(gòu)造器的注釋中寫的很明白了。mCookie是一個(gè)int值,保存了openDexFile方法的返回值,openDexFile方法是一個(gè)native方法,我們就不深入了。

四. 總結(jié)

幾種類加載器:

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

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

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

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

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

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

網(wǎng)友整理

注冊(cè)時(shí)間:

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

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

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

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

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

答題星2018-06-03

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

全階人生考試2018-06-03

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

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

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

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

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

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定