關於Android MultiDex的問題

這段時間研究了一下Android MultiDex,這個東西乍看起來很簡單,其實涉及到很多東西,本文就來講講這個,想到哪就說到哪。

分包主要是因爲包太大了,低端手機安裝可能會失敗,或者Dex加載的時候會崩潰,所以分成多個包,跟啓動相關的放在主Dex,其餘的不那麼緊急的放在副Dex,然後啓動的時候去動態加載。

首先來看看主Dex,所謂跟啓動相關的主要就是四大組件,所以四大組件直接引用到的類都要放在主Dex中,否則啓動時找不到類就悲劇了,畢竟動態加載副Dex可能會挺耗時的。Android Studio的build tool會自動幫你完成分包,將Android Manifest中定義的四大組件直接引用到的類都放在主Dex中,其餘的丟到副Dex。不過也不保證百分百沒問題,比如你用反射或者So引用副Dex中的類也許這個腳本就會瞎掉了。當然我們也可以自己寫個腳本去分析APP啓動需要引用到的類,方法是在APP啓動完畢時打出ClassLoader中所有已加載過的類就行了。

接下來再來說說這個副Dex加載的問題,如果這個副Dex比較大,加載起來可能挺耗時,爲什麼呢?耗時主要在於Dexopt優化。Dexopt優化時機通常有兩個,一個是APP安裝的時候,這時候只會優化主Dex。第二個是ClassLoader加載Dex時,不過如果已經優化過了就不會再優化了。所以在APP啓動時動態加載副Dex僅在第一次纔會挺耗時,之後就非常快了。現在關鍵就在於怎麼處理第一次加載Dex耗時的問題,因爲是在Application里加載的,容易ANR。我們可以另開一個線程來加載,不過可能還沒等到加載完,主Dex中就要引用到副Dex裏的類了,當然這個概率不會太大,因爲我們前面提到過主Dex裏已經給所有要直接引用的類都包含進去了,所以短時間內不會出岔子。但是事情就怕萬一,如果真出現這種情況,我們不能眼睜睜看着程序崩潰。

接下來就討論幾種解決方案:

第一種,美團的方案,攔截Activity的啓動過程,當發現要啓動的Activity還沒加載好的時候,直接改成跳到另一個LoadingActivity,等加載完畢後再跳到真正的目標。至於怎麼攔截Activity的啓動,可以參考我之前的文章如何Hook Activity的啓動。不過對於其餘的組件比如Service、廣播這種後臺型的就有點奇怪了,冷不丁會冒出一個LoadingActivity顯得不那麼自然。相比之下,下面的解決方案會好一些。

第二種,微信的解決方案,由於Dex只有沒有優化過的時候纔會調用Dexopt,所以也就是說第一次纔會耗時,之後都很快,那我們重點解決第一次就好了,只是第一次慢點用戶基本還可以接受。在Application啓動時如果發現Dex未優化,我們就啓動LoadingActivity,同時Application中繼續加載Dex,即便耗時也不會ANR。因爲LoadingActivity跑在新的進程,並且是當前的前臺進程,而Application成了後臺進程,不會導致ANR。當Dex加載完畢後就跳轉到真正的Launcher Activity,並殺掉LoadingActivity。這種解決方案總體來說還是不錯的,唯一的問題可能是LoadingActivity要啓動在一個新進程,也許會比較耗時,我們可以優化一下,判斷Dex加載會不會耗時,如果會耗時就放在一個子線程中加載,然後在當前進程中跳轉到LoadingActivity,等到Dex加載完了再跳到Launcher Activity。如果Dex加載不會耗時,就直接在主線程中加載了,然後也沒必要跳到Loading Activity了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章