Multidex(二)之 Dex 預載入優化
一、前言
在Multidex(一)之原始碼解析中我們介紹到MultiDex極有可能出現ANR(Application No Response)的問題,秒秒鐘卡死我們的應用,使用者肯定忍不了要怒解除安裝啊!作為追(被)求(逼)完(無)美(耐)的程式設計師哥哥,我們怎能作壁上觀?Google不做好的事情,我們就自己扛起來!那麼如何對MultiDex這個方案做優化讓它變成好同志呢?
本文就帶你實戰MultiDex的預載入優化。
二、分析
Multidex(一)之原始碼解析中分析過MultiDex第一次載入出現ANR的原因是因為提取Dex以及DexOpt這兩個過程都是耗時的操作,而且他們還都發生在主程式。稍等:主程式,ANR,腦袋裡好像閃現一道靈光,既然在主程式執行會產生ANR,那能不能換個程式執行呢?橘生淮南則為橘,生於淮北則為枳;換個程式說不定就有突破點。說幹就幹,憑藉程式設計師機智的大腦,分毫之間,一個優化方案的雛形已經瞭然於胸:App第一次啟動時單獨開一個額外優化的程式率先進行Dex提取以及DexOpt的操作,與此同時主程式在後臺等待,優化的程式執行完畢之後通知主程式繼續往下執行,主程式在執行MultiDex.install時發現已經是提前優化好了Dex,直接執行,非常快,毫秒級別,不會造成卡頓,愉快的往下繼續執行。
三、優化方案工作流程圖
四、程式碼實戰
在Application的attachBaseContext中執行優化方案;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
//只有主程式以及SDK版本5.0以下才走。
if (isMainProcess(Application.this) && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (!dexOptDone(base)) {
preLoadDex(base);
}
long startTime = System.currentTimeMillis();
MultiDex.install(this);
LogUtil.i(TAG,"MainProcessCostTime:"+(System.currentTimeMillis() - startTime));
}
}
/**
* 當前版本是否進行過DexOpt操作。
* @param context
* @return
*/
private boolean dexOptDone(Context context) {
SharedPreferences sp = context.getSharedPreferences(
DeviceUtil.getVersionName(context), MODE_MULTI_PROCESS);
return sp.getBoolean("dexoptdone", false);
}
/**
* 在單獨程式中提前進行DexOpt的優化操作;主程式進入等待狀態。
*
* @param base
*/
public void preLoadDex(Context base) {
Intent intent = new Intent(Application.this, PreLoadDexActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
base.startActivity(intent);
while (!dexOptDone(base)) {
try {
//主執行緒開始等待;直到優化程式完成了DexOpt操作。
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
然後在PreLoadDexActivity中執行優化的操作,完成後修改標示;
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
overridePendingTransition(0, 0);//取消掉系統預設的動畫。
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.predexlayout);
new Thread() {
@Override
public void run() {
super.run();
try {
long time = System.currentTimeMillis();
MultiDex.install(getApplication());
LogUtil.i("lz", "PreLoadDexActivityCostTime:" + (System.currentTimeMillis() - time));
SharedPreferences sp = getSharedPreferences(
DeviceUtil.getVersionName(PreLoadDexActivity.this), MODE_MULTI_PROCESS);
sp.edit().putBoolean("dexdone", true).commit();
killCurrentProcess();
} catch (Exception e) {
LogUtil.e("loadDex", e.getLocalizedMessage());
killCurrentProcess();
}
}
}.start();
}
在AndroidManifest中配置:
<activity android:name=".PreLoadDexActivity"
android:process=":preloaddex"
android:alwaysRetainTaskState= "false"
android:theme="@style/PreLoadStyle"
android:launchMode= "singleTask"
android:excludeFromRecents= "true"
android:screenOrientation= "portrait"
/>
執行結果如下:
可以通過Log看到,在優化程式中Dex的提取以及Dexopt的操作耗時近4秒,而在主程式的第二次執行則耗時16毫秒,耗時發生在優化程式中的執行緒中,主程式實際執行MultiDex.install的時候耗時極其短暫;再也不會出現ANR的困擾了。
- 第一次開啟App,會出現PreLoadDexActivity,略顯突兀,可以再應用的閃屏頁加上這段邏輯,根據標示判斷究竟執行正常邏輯還是優化的邏輯。
- 關於SharedPreferences程式間不安全的問題:此處的使用只是單向的讀寫,因而不會有這個場景。
五、問題
1、為什麼執行優化操作的時候判斷只有在主程式以及SDK版本5.0以下才執行呢?
如果App是多程式架構的話,Application會執行多次,這個優化過程無需執行多次;而在SDK版本5.0及以上,預設使用ART虛擬機器,與Dalvik的區別在於安裝時已經將全部的Class.dex轉換為了oat檔案,優化過程在安裝時已經完成;因此無需執行。
2、為什麼主程式此時不會ANR?
回憶下ANR的發生場景:Service、BroadCastReceiver、ContentProvider的TimeOut;輸入事件的TimeOut等。當出現ANR時,都會最終呼叫到AMS的appNotResponding()方法。
因為主程式此時已經進入後臺,不響應Android螢幕事件。同時也不存在以上發生ANR的場景,因此主程式在後臺Sleep,不會產生ANR。
3、在優化的程式中只是開啟了一個執行緒提前做了MultiDex的工作,那為什麼不直接在主程式中開啟一個子執行緒做同樣工作呢?
Good Question,不愧是善於思考的程式猿!在主程式中直接開啟一個子執行緒確實是可以避免ANR的問題,但是有沒有想到,此時主程式中呼叫到的類,可能會因為SecondaryDex的優化尚未完成或者沒有被加入到ClassLoader中而導致畫面太美不敢看的ClassNotFoundException。
相關文章
- MultiDex(三)之非同步載入優化IDE非同步優化
- 前端效能優化 --- 懶載入&預載入前端優化
- 前端效能優化 – 資源預載入前端優化
- 頁面載入效能之優化LCP優化
- 前端優化之圖片懶載入前端優化
- 前端效能優化之載入技術前端優化
- React Native 0.31 Bundle 預載入優化React Native優化
- 前端效能優化 - Resource Hints 資源預載入前端優化
- 延遲載入 Dex 檔案
- iOS效能優化之頁面載入速率iOS優化
- Android動態載入jar/dexAndroidJAR
- vue載入優化策略Vue優化
- 啟動優化之動態庫延遲載入優化
- 前端優化首屏載入前端優化
- 網頁效能優化之非同步載入js檔案網頁優化非同步JS
- Android外掛化(一):使用改進的MultiDex動態載入assets中的apkAndroidIDEAPK
- 【前端優化】js圖片懶載入及優化前端優化JS
- iOS效能優化 - 網路圖片載入優化iOS優化
- 八、Android效能優化之電量優化(二)Android優化
- tinker熱修復——dex補丁載入過程
- 預載入與智慧預載入(iOS)iOS
- 效能優化之元件懶載入: Vue Lazy Component 介紹優化元件Vue
- 前端效能優化——延遲載入和非同步載入前端優化非同步
- 優化 SPA 首屏載入速度優化
- Android優化--Fragment懶載入Android優化Fragment
- React 16 載入效能優化指南React優化
- node模組載入層級優化優化
- Oracle資料載入速度優化Oracle優化
- 載入GIF圖片優化方案優化
- 效能優化之關於畫素管道及優化(二)優化
- Android 效能優化(二)之佈局優化面面觀Android優化
- 前端效能和載入體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)前端優化記憶體
- MySQL之SQL優化詳解(二)MySql優化
- Android ART dex2oat 載入加速淺析Android
- 懶載入和預載入
- 效能優化之html、css、js三者的載入順序優化HTMLCSSJS
- VuePress 部落格之 SEO 優化(二)之重定向Vue優化
- Flutter圖片載入優化深入探索Flutter優化