1. 程式人生 > >MultiDex使用方法及由此導致的crash、ANR問題解決方案

MultiDex使用方法及由此導致的crash、ANR問題解決方案

Android開發的朋友,如果是在開發一款中大型應用時,都會碰到這麼一個問題,就是dex分拆問題, google給出的解決方案MultiDex。

現象:

有些APP本身功能比較多,再加上一些其它三方的SDK,慢慢的發現dex越來越大,直到有一天編譯出現如下錯誤:

Error:The number of method references in a .dex file cannot exceed 64K.
Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
  • 1
  • 2

錯誤原因比較明確了,開啟Gradle Console檢視詳細資訊,我們在一堆錯誤中找到如下提醒:

"UNEXPECTED TOP-LEVEL EXCEPTION:\ncom.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536\n\tat com.android.dx.merge.DexMerger$6.updateIndex(DexMerger
  • 1

原因:

問題原因是因為早期Android系統設計時用一個short來表示dex裡的每個method的id,我們知道short的上限是64K,所以就遺留了這樣一個問題,既然存在了,肯定要找方法解決,Google剛開始給出建議是使用proguard,但是再怎麼proguard也還是會突破64K的,所以後面Google給出了MultiDex方案。就是我們經常看到的一個apk裡,有classes.dex, classes2.dex,甚至還有classes3.dex等。

MultiDex實現步驟:

用這種方法來突破64K的method id數量的限制,具體實現步驟如下:
1. 在Module的build.gradle裡新增
multiDexEnabled true

例如:

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "x.x.x"
        minSdkVersion 15
        targetSdkVersion 22
        versionCode 17
        versionName "1.2.9"
//新增這一行 multiDexEnabled true } }

2. 接著在Module的build.gradle裡新增

dependencies {
  compile 'com.android.support:multidex:1.0.0'
}
  • 1
  • 2
  • 3

3.第三步有兩種情況,
1)如果你的apk沒有定義application,則在AndroidManifest.xml裡的application裡做如下修改:
新增MultiDexApplication

<application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        android:name="android.support.multidex.MultiDexApplication"
        tools:replace="android:icon, android:name"
       >
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2)如果apk有自己的appliation,比如叫MyApplication,則在MyApplication實現里加如下程式碼:

 @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }

至此,MultiDex的編譯配置已經完成,你只要重新編譯即可,apk就會生成兩個(或多個) dex。如果有興趣瞭解下MultiDex的實現原理,可以檢視原始碼,https://android.googlesource.com/platform/frameworks/multidex 原始碼相對比較簡單的。基本原理是使用java的class loader,這個在一些熱修復技術上也在使用,比如企鵝工廠開源的Tinker。

存在的坑及解決方法:

這個問題的表現除了報VerifyError外,還有可能報Could not find class,NoClassDefFoundError, Could not find method等。是因為我們在main dex中呼叫的函式或類被放在了classes2.dex中,而在classes2.dex還沒有被完全載入前,呼叫這些api就會導致這種問題。要確認是否是這個問題導致的錯誤,我們可以檢視:
app\build\intermediates\multi-dex\debug\maindexlist.txt 這個文字檔案,這裡列出來的類都會被放在主dex中,那麼問題是我們要如何解決這個問題呢?
解決方案:
首先,我們來看下在編譯過程中,multidex是做了哪幾步,這個開啟Gradle Console可以找到multidex相關的步驟,其中一個步驟是關鍵就是生成maindexlist.txt的步驟:createDebugMainDexClassList就是這裡生成maindexlist.txt 的,但是這個檔案直接修改又沒有什麼用,因為每次編譯都會重新生成一次的,筆者在實踐者發現可以用自定義的方式:multiDexKeepFile file(‘multiDexKeep.txt’)
例子:

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"


    defaultConfig {
        applicationId "x.x.x"
        minSdkVersion 15
        targetSdkVersion 22
        versionCode 17
        versionName "1.2.9"

        multiDexEnabled true

        //新增這一行
        multiDexKeepFile file('multiDexKeep.txt')
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

內容和上面提到的createDebugMainDexClassList生成的maindexlist.txt一樣即可,記得把這個multiDexKeep.txt檔案放在app目錄 下。multiDexKeep.txt內容可以如下:

com/test/Util.class
com/test/help/b.class
  • 1
  • 2

實測起作用,被keep的class全都留在了classes.dex中(release版本要配合mapping使用)。

2. ANR
也就是我們常說的卡頓,為什麼會卡頓呢?這裡因為我們把multidex的install放在了attachBaseContext中,而這個呼叫又是在MainActivity的onCreate之前的,所以如果2.dex,3.dex第一次載入時間很長(生成odex檔案會耗費一定的時間), 就有可能會導致第一次啟動卡上一小會(實測在Dalvik手機上一般classes.dex比較小的情況下,卡頓不明顯,線上就不好說了)。

解決方法是在 APP第一次啟動時(解除安裝、重灌都會做一遍2odex,具體可以檢視/data/data//code_cache/secondary-dexes/目錄下的odex檔案,所以這裡判斷要準確),把install放到非同步執行緒裡去做。這是很多網上的解決方法,但是如果這樣做,你就必須再寫一個類似initAfterDex2Installed(),來保證2.dex裡的類不會提前被呼叫到,或者輸出一個啟動介面,停留幾秒的樣子,比如很多APP啟動都有開機廣告,或者開機畫面,以此來解決app在2.dex載入之前部分功能無法使用的問題。網上大多是這種解決方案。本人測試後發現,如果MultiDex.install(this),放在後面或者非同步來做的話,在MainActivity裡的onCreate函式:
setContentView這裡就出錯了,堆疊如下:

java.lang.NoClassDefFoundError: android.support.v7.appcompat.R$attr
                                                       at android.support.v7.app.AppCompatDelegateImplV7.ensureSubDecor(AppCompatDelegateImplV7.java:289)
                                                       at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:246)
                                                       at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:106)
                                                       at com.cn.x.x.MainActivity.onCreate(MainActivity.java:86)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

很明顯android.support.v7.appcompat.R$attr在classes2.dex中,在呼叫時,還沒有完成classes2.dex的載入,所以如果要解決的話,要不把這個類放到maindex中,要不讓MainActivity的onCreate函式延遲呼叫。如果其它類還有類似問題,就要一個一個的試,成本有點高,有可能在不同系統,不同手機上還會出各種奇芭錯誤。由此看來,install延期載入方案並不合適。為了說明問題,也分析了網易幾款大的產品,比如新聞、雲音樂,全部是使用的mutlidex方式,都沒有延期載入,全部是在application的attachBaseContext直接呼叫install.

那麼我們要如何解決 2.dex, 3.dex在第一次啟動apk時,有可能產生的耗時呢?

1)重新設計,或者在開發之前就設計好,使用外掛化的方式來解決maindex的問題,把一些功能做成外掛,保證這些dex在首屏啟動時不需要被載入
2) 自己實現多dex框架,有興趣可以看下微信的實現框架,並沒有使用MultiDex,而是使用自己的Tinker(一種動態載入dex的方案,也被用於熱更新)
微信使用的是TinkerAppliation. 另外一個例子是手機QQ的實現技術,手Q里居然有classes6.dex,也就是總共有6個dex,感興趣的朋友可以分析下手Q的實現方案,筆者動態看過,基本上也是在手Q啟動介面還沒出來時,所有的dex會全部完成2odex的轉換,在手機上第一次執行還是會花費不少時間的。而且筆者測試了很多手機,基本上2個dex不會發生anr,當然真實情況怎麼樣,還是要放到線上才能說明問題,也就是接收眾多手機,不同品牌,不同效能的手機測試才知道,好在有一些知名公司的質量跟蹤平臺可以線上捕捉這些ANR,比如網易雲捕

3) ART以後的實際情況是,不管你分成幾個dex,在安裝時都已經全部完成OAT的轉換,這樣分dex至少在ART以後也就是5.0(部分4.4機型可以選ART模式)以後不存在呼叫install佔用時間的問題,而且實際測試也是在5.0以後,即使不呼叫install(this)也能正常工作,為了證實,我們拿網易新聞測試,在安裝完後,在目錄 /data/dalvik-cache//[email protected]@[email protected]@classes.dex,這個oat檔案有84M,所以你可以知道
為什麼第一次安裝相比 dalivk慢了。
開啟這個檔案,字尾雖然還是dex,其實是一個ELF檔案,也就是OAT檔案。
這裡寫圖片描述
classes.dex在這裡。
這裡寫圖片描述

這裡寫圖片描述
classes2.dex, classes3.dex都已經被優化成OAT檔案了。

這些處理都已經在安裝的時候完成,就不存在啟動時再對classes2.dex和classes3.dex進行處理了,避免了第一次啟動可能導致的ANR.

總結

總結一下就是我們可以通過自定義maindexlist來控制哪些類一定出現在main dex中,這樣可以避免crash;
而針對ANR還是不建議使用非同步載入,合理設計和外掛形式會比較合理,如果大家有其它更好的方法可以討論。
不過在ART以後,不管是classes.dex還是classes2.dex, classes3.dex在安裝時就已經完成了OAT的轉換了,由分包導致的ANR的可能性就小了很多。

原文:https://blog.csdn.net/zhuobattle/article/details/52958364

相關推薦

MultiDex使用方法由此導致crashANR問題解決方案

Android開發的朋友,如果是在開發一款中大型應用時,都會碰到這麼一個問題,就是dex分拆問題, google給出的解決方案MultiDex。現象:有些APP本身功能比較多,再加上一些其它三方的SDK,慢慢的發現dex越來越大,直到有一天編譯出現如下錯誤:Error:The

linux遠端拷貝檔案方法not a regular file 錯誤解決方案

操作步驟: 1、搭建區域網:參考以下文章中網路配置部分:http://dblab.xmu.edu.cn/blog/install-hadoop-cluster/ 2、搭建好區域網後,可以使用scp命令完成遠端檔案拷貝操作 命令格式:  scp [引數] [原路徑] [目標路

icheck結合datatable使用方法實現全選反選功能

var table = $("#user_index").DataTable({ order: [ ["1", 'asc'] ], //按照發布時間降序排序 page: false, serverSide:true,

python基礎五:元組的index,del,count方法字典的修改增加刪除(delclear)遍歷(keysvaluesitems)

元組:index、del、count的方法。index的用法:用來查詢元組中元素對應的下標。格式:元組變數名.index(查詢的元素)例項:    del的用法:用來刪除整個元組。格式:del       元組變數名例項:      count的用法:用來統計元組中指定元素的

方法數超過限制,Google官方解決方案連結:https://developer.android.com/studio/build/multidex.html

方法數超過限制,Google官方解決方案連結:https://developer.android.com/studio/build/multidex.html 對了,如果參考第三種方案,發現 Multidex.install(this); 這一行程式碼總是報錯,嘗試用這行程式碼: Mu

MongoDB 索引的建立注意事項以及建索引導致鎖庫的解決方案

                          MongoDB索引的建立注意事項        在資料量超大的情形下,

java oracle 2顯示為2.0解決方法 0.2顯示.2的解決問題

今天遇到個顯示問題,爬坑之餘,與大家分享一下解決方法。首先資料庫裡取出的資料是需要進行計算的,首選了型別double。 這樣客戶想要的2顯示為2.0就不能正常展現了,oh my god .上帝的需求必須滿足。這樣本人將double型別換成了String型別。 第二個坑就出現了,0.2變成了.

Android6.0以上應用在長時間在後臺,因為記憶體不足導致系統回收記憶體,當再次啟動應用出現Fragment重疊或者空白異常解決方案(提供模擬記憶體不足導致系統回收記憶體的方案)。

  Android6.0以上應用在長時間在後臺,因為記憶體不足導致系統回收記憶體,當再次啟動應用出現Fragment重疊或者空白解決方案。首先提供一個方法模擬記憶體不足導致系統回收記憶體的方案:開啟Android Studio -->Tools-->Android

檢測網站被***的方法預防網站被黑的解決方法

log 大量 知識 技術 打不開 我們 技術人 fff 遠程 網站被***,首先牽扯到的就是網站的開發語言,包括了代碼語言,以及數據庫語言,目前大多數網站都是使用的PHP,JAVA,.net語言開發,數據庫使用的是mysql,oracle等數據庫,那麽網站被***了該怎麽辦

JWPlayer 7的正確使用方法Flash plugin failed to load解決方法

一.JW Player 簡介 JW Player是當今最流行的開源Flah網頁播放器,可播放Adobe Flash Player所支援的媒體,具體包括:FLV、MP4、MP3、AAC、JPG、PNG和

Asp.Net程序根目錄下文件夾操作導致Session失效的解決方案

rgs get bject process simple exce cep clas .net 1、配置web.config <system.web> <sessionState mode="StateServer" stateConnectio

Spring的Service調用本類方法聲明式事務無效的解決方案

{} 解決 ostc oid color wire ont spa 出了 示例: class Test{ public void a(){ b(); } @Transactional pubic void b(){} }

Spring配置文件xsi:schemaLocation無法解析導致啟動失敗的解決方案

解析 線上 png 16px 沒有 不能 圖片 ima cati   今天遇到過情況,spring的配置文件在本地讀取沒有問題,扔到線上服務器運行就報無法解析xml,找了很久問題,發現是因為線上服務器無法上網,導致無法下載相關的xsd文件,沒辦法不能上網就只有使用本地的xs

linux 關於Apache默認編碼錯誤 導致網站亂碼的解決方案

IE 如何 這不 而是 TP 策略 接收 art 原因 Apache默認編碼UTF-8在解析A網站的時候沒有任何問題,當運行B網站時出現的"蝌蚪文"亂碼問題 最近經常有同學在使用LAMP/WAMP時,遇到這樣的編碼錯誤問題: A網站程序編碼UTF-8編碼安裝成功,運行成

服務器數據恢復的正確方法/服務器硬盤故障的解決方案

條件 系統類型 無法 服務 watermark 備份操作 com alt mkfs [服務器數據恢復原因推斷] 服務器數據丟失情況很多,通常無法明確服務器數據丟失的原因,常見的丟失原因有:服務器硬盤出現故障,管理員或者服務器自動進行fsck操作,這一操作可能造成更加嚴重數據

針對惠普伺服器SNMP採集頻繁,導致服務停止的解決方案

運維軟體:zabbix採集方式:snmp採集裝置:惠普-DL380_Gen9-伺服器採集週期:根據指標要求頻率有5分鐘到1天主要命令:reset /map1樂維服務中發現,snmp採集惠普伺服器一段時間後,zabbix提示連線失敗,重啟ilo後又可以重新採集,我們可以利用這一點做一

spring-boot不同包結構下,同名類衝突導致服務啟動失敗解決方案

專案背景:   兩個專案的包結構和類名都很多相同,於是開始考慮使用加一級包進行隔離,類似於這種結構 但是在啟動的過程中,丟擲來這樣的異常: 1 2 3 4 5 6 7 8 9 Caused by: org.springframework.

全面了解移動端DNS域名劫持等雜癥:原理根源HttpDNS解決方案

快的 cpi 域名服務器 來講 基本 淺析 易懂 amp 推廣 1、引言 對於互聯網,域名是訪問的第一跳,而這一跳很多時候會“失足”(尤其是移動端網絡),導致訪問錯誤內容、失敗連接等,讓用戶在互聯網上暢遊的爽快瞬間消失。 而對於這關鍵的第一跳,包括鵝廠在內的國內互聯

Android 修改系統字型大小,導致頁面展示異常解決方案

Android系統預設是允許修改系統本身的字型大小的,導致頁面展示異常。 頁面展示異常是因為系統字型大小影響到了程式中字型的單位sp,所以解決此問題的方案1是,將佈局中字型大小的單位sp換成dp 解決方案2:重寫Activity或Application中的getResou

手把手教你進行R語言的安裝安裝過程中相關問題解決方案

這篇文章旨在為R語言的新手鋪磚引路,行文相對基礎,希望對在R語言安裝上有問題的小夥伴提供幫助和指引。一、什麼是 R 語言R 程式語言被廣泛應用在統計科學和商業領域。 在各種程式語言排名中 R 語言的排名都很靠前。 它是一款集成了資料操作、統計,以及視覺化功能的優秀開源軟體。免費,開源是 R 重要的特點。二