国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
[轉(zhuǎn)]Android dex分包方案

標(biāo)簽:

轉(zhuǎn)載自:https://m.oschina.net/blog/308583

當(dāng)一個(gè)app的功能越來越復(fù)雜,代碼量越來越多,也許有一天便會突然遇到下列現(xiàn)象

1. 生成的apk在2.3以前的機(jī)器無法安裝,提示INSTALL_FAILED_DEXOPT

2. 方法數(shù)量過多,編譯時(shí)出錯(cuò),提示:

Conversion to Dalvik format failed:Unable to execute dex: method ID not in [0, 0xffff]: 65536  

 

出現(xiàn)這種問題的原因是

1. Android2.3及以前版本用來執(zhí)行dexopt(用于優(yōu)化dex文件)的內(nèi)存只分配了5M

2. 一個(gè)dex文件最多只支持65536個(gè)方法。

 

針對上述問題,也出現(xiàn)了諸多解決方案,使用的最多的是插件化,即將一些獨(dú)立的功能做成一個(gè)單獨(dú)的apk,當(dāng)打開的時(shí)候使用DexClassLoader動(dòng)態(tài)加載,然后使用反射機(jī)制來調(diào)用插件中的類和方法。這固然是一種解決問題的方案:但這種方案存在著以下兩個(gè)問題:

1. 插件化只適合一些比較獨(dú)立的模塊;

2. 必須通過反射機(jī)制去調(diào)用插件的類和方法,因此,必須搭配一套插件框架來配合使用;

 

由于上述問題的存在,通過不斷研究,便有了dex分包的解決方案。簡單來說,其原理是將編譯好的class文件拆分打包成兩個(gè)dex,繞過dex方法數(shù)量的限制以及安裝時(shí)的檢查,在運(yùn)行時(shí)再動(dòng)態(tài)加載第二個(gè)dex文件中。faceBook曾經(jīng)遇到相似的問題,具體可參考:

https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920

文中有這么一段話:

However, there was no way we could break our app up this way--too many of our classes are accessed directly by the Android framework. Instead, we needed to inject our secondary dex files directly into the system class loader。

文中說得比較簡單,我們來完善一下該方案:除了第一個(gè)dex文件(即正常apk包唯一包含的Dex文件),其它dex文件都以資源的方式放在安裝包中,并在Application的onCreate回調(diào)中被注入到系統(tǒng)的ClassLoader。因此,對于那些在注入之前已經(jīng)引用到的類(以及它們所在的jar),必須放入第一個(gè)Dex文件中。

 

下面通過一個(gè)簡單的demo來講述dex分包方案,該方案分為兩步執(zhí)行:

整個(gè)demo的目錄結(jié)構(gòu)是這樣,我打算將SecondActivity,MyContainer以及DropDownView放入第二個(gè)dex包中,其它保留在第一個(gè)dex包。

一、編譯時(shí)分包

整個(gè)編譯流程如下:

 

除了框出來的兩Target,其它都是編譯的標(biāo)準(zhǔn)流程。而這兩個(gè)Target正是我們的分包操作。首先來看看spliteClasses target。

 

由于我們這里僅僅是一個(gè)demo,因此放到第二個(gè)包中的文件很少,就是上面提到的三個(gè)文件。分好包之后就要開始生成dex文件,首先打包第一個(gè)dex文件: 

 

由這里將${classes}(該文件夾下都是要打包到第一個(gè)dex的文件)打包生成第一個(gè)dex。接著生成第二個(gè)dex,并將其打包到資資源文件中:

 

可以看到,此時(shí)是將${secclasses}中的文件打包生成dex,并將其加入ap文件(打包的資源文件)中。到此,分包完畢,接下來,便來分析一下如何動(dòng)態(tài)將第二個(gè)dex包注入系統(tǒng)的ClassLoader。

 

二、將dex分包注入ClassLoader

這里談到注入,就要談到Android的ClassLoader體系。

 

由上圖可以看出,在葉子節(jié)點(diǎn)上,我們能使用到的是DexClassLoader和PathClassLoader,通過查閱開發(fā)文檔,我們發(fā)現(xiàn)他們有如下使用場景:

1. 關(guān)于PathClassLoader,文檔中寫到: Android uses this class for its system class loader and for its application class loader(s),

由此可知,Android應(yīng)用就是用它來加載;

2. DexClass可以加載apk,jar,及dex文件,但PathClassLoader只能加載已安裝到系統(tǒng)中(即/data/app目錄下)的apk文件。

 

知道了兩者的使用場景,下面來分析下具體的加載原理,由上圖可以看到,兩個(gè)葉子節(jié)點(diǎn)的類都繼承BaseDexClassLoader中,而具體的類加載邏輯也在此類中:

BaseDexClassLoader:  

[java] view plaincopy

  1. @Override  

  2. protected Class<?> findClass(String name) throws ClassNotFoundException {  

  3.     List<Throwable> suppressedExceptions = new ArrayList<Throwable>();  

  4.     Class c = pathList.findClass(name, suppressedExceptions);  

  5.     if (c == null) {  

  6.         ClassNotFoundException cnfe = new ClassNotFoundException("Didn‘t find class \"" + name + "\" on path: " + pathList);  

  7.         for (Throwable t : suppressedExceptions) {  

  8.             cnfe.addSuppressed(t);  

  9.        }  

  10.         throw cnfe;  

  11.     }  

  12.      return c;  

  13. }  

 

由上述函數(shù)可知,當(dāng)我們需要加載一個(gè)class時(shí),實(shí)際是從pathList中去需要的,查閱源碼,發(fā)現(xiàn)pathList是DexPathList類的一個(gè)實(shí)例。ok,接著去分析DexPathList類中的findClass函數(shù),

DexPathList:

[java] view plaincopy

  1. public Class findClass(String name, List<Throwable> suppressed) {  

  2.     for (Element element : dexElements) {  

  3.         DexFile dex = element.dexFile;  

  4.         if (dex != null) {  

  5.             Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);  

  6.             if (clazz != null) {  

  7.                 return clazz;  

  8.             }  

  9.         }  

  10.    }  

  11.     if (dexElementsSuppressedExceptions != null) {  

  12.         suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));  

  13.     }  

  14.     return null;  

  15. }  

上述函數(shù)的大致邏輯為:遍歷一個(gè)裝在dex文件(每個(gè)dex文件實(shí)際上是一個(gè)DexFile對象)的數(shù)組(Element數(shù)組,Element是一個(gè)內(nèi)部類),然后依次去加載所需要的class文件,直到找到為止。

看到這里,注入的解決方案也就浮出水面,假如我們將第二個(gè)dex文件放入Element數(shù)組中,那么在加載第二個(gè)dex包中的類時(shí),應(yīng)該可以直接找到。

帶著這個(gè)假設(shè),來完善demo。

在我們自定義的BaseApplication的onCreate中,我們執(zhí)行注入操作:

[java] view plaincopy

  1. public String inject(String libPath) {  

  2.     boolean hasBaseDexClassLoader = true;  

  3.     try {  

  4.         Class.forName("dalvik.system.BaseDexClassLoader");  

  5.     } catch (ClassNotFoundException e) {  

  6.         hasBaseDexClassLoader = false;  

  7.     }  

  8.     if (hasBaseDexClassLoader) {  

  9.         PathClassLoader pathClassLoader = (PathClassLoader)sApplication.getClassLoader();  

  10.         DexClassLoader dexClassLoader = new DexClassLoader(libPath, sApplication.getDir("dex", 0).getAbsolutePath(), libPath, sApplication.getClassLoader());  

  11.         try {  

  12.             Object dexElements = combineArray(getDexElements(getPathList(pathClassLoader)), getDexElements(getPathList(dexClassLoader)));  

  13.             Object pathList = getPathList(pathClassLoader);  

  14.             setField(pathList, pathList.getClass(), "dexElements", dexElements);  

  15.             return "SUCCESS";  

  16.         } catch (Throwable e) {  

  17.             e.printStackTrace();  

  18.             return android.util.Log.getStackTraceString(e);  

  19.         }  

  20.     }  

  21.     return "SUCCESS";  

  22. }   

這是注入的關(guān)鍵函數(shù),分析一下這個(gè)函數(shù):

參數(shù)libPath是第二個(gè)dex包的文件信息(包含完整路徑,我們當(dāng)初將其打包到了assets目錄下),然后將其使用DexClassLoader來加載(這里為什么必須使用DexClassLoader加載,回顧以上的使用場景),然后通過反射獲取PathClassLoader中的DexPathList中的Element數(shù)組(已加載了第一個(gè)dex包,由系統(tǒng)加載),以及DexClassLoader中的DexPathList中的Element數(shù)組(剛將第二個(gè)dex包加載進(jìn)去),將兩個(gè)Element數(shù)組合并之后,再將其賦值給PathClassLoader的Element數(shù)組,到此,注入完畢。

 

現(xiàn)在試著啟動(dòng)app,并在TestUrlActivity(在第一個(gè)dex包中)中去啟動(dòng)SecondActivity(在第二個(gè)dex包中),啟動(dòng)成功。這種方案是可行。

 

但是使用dex分包方案仍然有幾個(gè)注意點(diǎn):

1. 由于第二個(gè)dex包是在Application的onCreate中動(dòng)態(tài)注入的,如果dex包過大,會使app的啟動(dòng)速度變慢,因此,在dex分包過程中一定要注意,第二個(gè)dex包不宜過大。

2. 由于上述第一點(diǎn)的限制,假如我們的app越來越臃腫和龐大,往往會采取dex分包方案和插件化方案配合使用,將一些非核心獨(dú)立功能做成插件加載,核心功能再分包加載。

[轉(zhuǎn)]Android dex分包方案

標(biāo)簽:

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Android 插件化原理解析(6、下)
Android App 如何動(dòng)態(tài)加載類
android dex生成過程,Android加固原理研究 DEX文件格式分析
Android Patch 方案與持續(xù)交付
Android熱更新實(shí)現(xiàn)原理
Android分包MultiDex源碼分析
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服