免费A级毛片无码专区网站-成人国产精品视频一区二区-啊 日出水了 用力乖乖在线-国产黑色丝袜在线观看下-天天操美女夜夜操美女-日韩网站在线观看中文字幕-AV高清hd片XXX国产-亚洲av中文字字幕乱码综合-搬开女人下面使劲插视频

Android10 dex2oat實踐

最近看到一篇博客:Android性能優(yōu)化之Android 10+ dex2oat實踐 , 對這個優(yōu)化很感興趣,打算研究研究能否接入到項目中 。不過該博客只講述了思路 , 沒有給完整源碼 。本項目參考該博客的思路 , 實現(xiàn)了該方案 。
源碼地址:https://github.com/carverZhong/DexOpt
一、dex2oat 詳解以下是官方對于dex2oat的解釋:
ART 使用預(yù)先 (AOT) 編譯 , 并且從 Android 7.0(代號 Nougat , 簡稱 N)開始結(jié)合使用 AOT、即時 (JIT) 編譯和配置文件引導(dǎo)型編譯 。所有這些編譯模式的組合均可配置 , 我們將在本部分中對此進行介紹 。例如 , Pixel 設(shè)備配置了以下編譯流程:
  1. 最初安裝應(yīng)用時不進行任何 AOT 編譯 。應(yīng)用前幾次運行時 , 系統(tǒng)會對其進行解譯 , 并對經(jīng)常執(zhí)行的方法進行 JIT 編譯 。
  2. 當(dāng)設(shè)備閑置和充電時 , 編譯守護程序會運行 , 以便根據(jù)在應(yīng)用前幾次運行期間生成的配置文件對常用代碼進行 AOT 編譯 。
  3. 下一次重新啟動應(yīng)用時將會使用配置文件引導(dǎo)型代碼 , 并避免在運行時對已經(jīng)過編譯的方法進行 JIT 編譯 。在應(yīng)用后續(xù)運行期間經(jīng)過 JIT 編譯的方法將會添加到配置文件中 , 然后編譯守護程序?qū)@些方法進行 AOT 編譯 。
ART 包括一個編譯器(dex2oat 工具)和一個為啟動 Zygote 而加載的運行時 (libart.so) 。dex2oat 工具接受一個 APK 文件 , 并生成一個或多個編譯工件文件 , 然后運行時將會加載這些文件 。文件的個數(shù)、擴展名和名稱因版本而異 , 但在 Android 8 版本中 , 將會生成以下文件:
.vdex:其中包含 APK 的未壓縮 DEX 代碼 , 以及一些旨在加快驗證速度的元數(shù)據(jù) 。.odex:其中包含 APK 中已經(jīng)過 AOT 編譯的方法代碼 。.art (optional):其中包含 APK 中列出的某些字符串和類的 ART 內(nèi)部表示 , 用于加快應(yīng)用啟動速度 。(配置 ART)
也就是說 , dex2oat可以觸發(fā)APK的AOT編譯 , 并生成對應(yīng)的產(chǎn)物 , APP運行時會加載這些文件 。執(zhí)行過AOT編譯的產(chǎn)物能加快啟動速度、代碼執(zhí)行效率 。
二、代碼實現(xiàn)具體原理還是參考博客:Android性能優(yōu)化之Android 10+ dex2oat實踐 。這里說下實現(xiàn)上的細節(jié) 。博客的思路是通過一些手段觸發(fā)系統(tǒng)來進行dex2oat 。
1.整體思路
  1. PackageManagerShellCommand.runCompile方法可以觸發(fā)Secondary Apk進行dex2oat , 但是Secondary Apk需要先注冊 。
  2. 注冊的邏輯在IPackageManagerImpl.registerDexModule , 其中IPackageManagerImplPackageManagerService的內(nèi)部類 , 并繼承了IPackageManager.Stub 。
  3. 最后 , 再執(zhí)行PackageManagerShellCommand.runreconcileSecondaryDexFiles反注冊 , 就大功告成了 。
所以整體分三步走:
  • 注冊Secondary Apk
  • 執(zhí)行dex2oat
  • 反注冊Secondary Apk
2.注冊Secondary ApkIPackageManager是個AIDL接口 , 而應(yīng)用中的ApplicationPackageManage剛好持有這個AIDL接口 , 因此可以通過其調(diào)用registerDexModule方法 。
為此 , 可以通過反射調(diào)用registerDexModule方法 。以下是核心實現(xiàn):
// 注冊Secondary Apkprivate fun registerDexModule(apkFilePath: String): Boolean {try {val callbackClazz = ReflectUtil.findClass("android.content.pm.PackageManager\$DexModuleRegisterCallback")ReflectUtil.callMethod(getCustomPM(),"registerDexModule",arrayOf(apkFilePath, null),arrayOf(String::class.java, callbackClazz))return true} catch (thr: Throwable) {Log.e(TAG, "registerDexModule: thr.", thr)}return false}/** * 創(chuàng)建一個自定義的 PackageManager , 避免影響正常的 PackageManager */private fun getCustomPM(): PackageManager {val customPM = cacheCustomPMif (customPM != null && cachePMBinder?.isBinderAlive == true) {return customPM}val pmBinder = getPMBinder()val pmBinderDynamicProxy = Proxy.newProxyInstance(context.classLoader, ReflectUtil.getInterfaces(pmBinder::class.java)) { _, method, args ->if ("transact" == method.name) {// FLAG_ONEWAY => NONE.args[3] = 0}method.invoke(pmBinder, *args)}val pmStubClass = ReflectUtil.findClass("android.content.pm.IPackageManager\$Stub")val pmStubProxy = ReflectUtil.callStaticMethod(pmStubClass,"asInterface",arrayOf(pmBinderDynamicProxy),arrayOf(IBinder::class.java))val contextImpl = if (context is ContextWrapper) context.baseContext else contextval appPM = createAppPM(contextImpl, pmStubProxy!!)cacheCustomPM = appPMreturn appPM}

經(jīng)驗總結(jié)擴展閱讀