五、CLASS_ISPREVERIFIED
運行一下Demo,報以下錯誤。(AndroidStudio 2.0 可能不會報錯,需要打包的時候才會出現錯誤,這是Instant run 導致的)

dexElements 的length = 2,看來我們的patch_dex 已經成功添加進去了。
但是從黃色框框和黃色框上面那一段log 提示中可以看出,MainActivity 引用了Cat,但是發現他們在不同的Dex 中。
看到這里可能就會問:
為什么之前那么多項目都采用分包方案,但是卻不會出現這個錯誤呢?
我在這里總結了一個過程,想知道詳細分析過程的請看QQ 空間開發團隊的原文。
在apk 安裝的時候,虛擬機會將dex 優化成odex 后才拿去執行。在這個過程中會對所有class 一個校驗。
校驗方式:假設A 該類在它的static 方法,private 方法,構造函數,override方法中直接引用到B 類。如果A 類和B 類在同一個dex 中,那么A 類就會被打上CLASS_ISPREVERIFIED 標記
。
被打上這個標記的類不能引用其他dex 中的類,否則就會報圖中的錯誤在我們的Demo 中,MainActivity 和Cat 本身是在同一個dex 中的,所以MainActivity 被打上了CLASS_ISPREVERIFIED。而我們修復bug 的時候卻引用了另外一個dex 的Cat.class,所以這里就報錯了
。
而普通分包方案則不會出現這個錯誤,因為引用和被引用的兩個類一開始就不在同一個dex 中,所以校驗的時候并不會被打上CLASS_ISPREVERIFIED
。
補充一下第二條:A 類如果還引用了一個C 類,而C 類在其他dex 中,那么A類并不會被打上標記。換句話說,只要在static 方法,構造方法,private 方法,override 方法中直接引用了其他dex 中的類,那么這個類就不會被打上
CLASS_ISPREVERIFIED 標記。
5.1 解決方案
根據上面的第六條,我們只要讓所有類都引用其他dex 中的某個類就可以了。
下面是QQ 控件給出的解決方案

在所有類的構造函數中插入這行代碼System.out.println(AntilazyLoad.class);這樣當安裝apk 的時候,classes.dex 內的類都會引用一個在不相同dex 中的AntilazyLoad 類,這樣就防止了類被打上CLASS_ISPREVERIFIED 的標志了,只要沒被打上這個標志的類都可以進行打補丁操作。
hack.dex 在應用啟動的時候就要先加載出來,不然AntilazyLoad 類會被標記為不存在,即使后面再加載hack.dex,AntilazyLoad 類還是會提示不存在。該類只要一次找不到,那么就會永遠被標上找不到的標記了。
我們一般在Application 中執行dex 的注入操作,所以在Application 的構造中不能加上`System.out.println(AntilazyLoad.class);`這行代碼,因為此時hack.dex 還沒有加載進來,AntilazyLoad 并不存在。
之所以選擇構造函數是因為他不增加方法數,一個類即使沒有顯式的構造函數,也會有一個隱式的默認構造函數。
5.2 插入代碼的難點
1.首先在源碼中手動插入不太可行,hack.dex 此時并沒有加載進來,AntilazyLoad.class 并不存在,編譯不通過。
2.所以我們需要在源碼編譯成字節碼之后,在字節碼中進行插入操作。對字節碼進行操作的框架有很多,但是比較常用的則是ASM 和javaassist
3.但AndroidStudio 是使用Gradle 構建項目,編譯-打包都是自動化的,我們怎么操作呢。
- 第一章 熱修復設計
- 第一節、AOT/JIT & dexopt 與dex2oat
- 一、AOT/JIT
- 二、dexopt 與dex2oat
- 第二節、熱修復設計之CLASS_ISPREVERIFIED 問題
- 一、前言
- 二、建立測試Demo
- 三、制作補丁
- 四、加載補丁
- 五、CLASS_ISPREVERIFIED
- 第三節、熱修復設計之熱修復原理
- 一、Android 熱修復
- 二、Android 虛擬機和編譯加載順序
- 三、混合模式的理解
- 四、源碼類到機器執行的文件過程
- 五、補丁包
- 六、類補丁生效原理
- 七、Davlik 虛擬機的限制
- 八、Davlik Class resolved by unexpected DEX: 限制和處理方式
- 九、類加載器的雙親委派加載機制
- 第四節、Tinker 的集成與使用(自動補丁包生成)
- 一、簡述
- 二、Tinker 組件依賴
- 三、Tinker 的配置及任務
- 四、Tinker 封裝與拓展
- 五、編寫Application 的代理類
- 六、常用API
- 七、測試
- 八、細節
- 第二章 插件化設計
- 第一節、Class 文件與Dex 文件的結構解讀
- 一、Class 文件
- 二、Dex 文件
- 三、Class 文件和Dex 文件對比
- 第二節、Android 資源加載機制詳解
- 第三節、四大組件調用原理
- 第四節、so 文件加載機制
- 第五節、Android 系統服務實現原理
- 第三章 組件化框架設計
- 第一節、阿里巴巴開源路由框——ARouter 原理分析
- 第二節、APT 編譯時期自動生成代碼&動態類加載
- 第三節、Java SPI 機制
- 第四節、AOP&IOC
- 第五節、手寫組件化架構
- 第四章 圖片加載框架
- 第一節 圖片加載框架選型
- 第二節 Glide 原理分析
- 第三節 手寫圖片加載框架實戰
- 第五章 網絡訪問框架設計
- 第一節 網絡通信必備基礎
- 第二節 OkHttp 源碼解讀
- 第三節 Retrofit2 源碼解析
- 第六章 RXJava響應式編程框架設計
- 第一節 RXJava之鏈式調用
- 第二節 RXJava之擴展的觀察者模式
- 第三節 RXJava之事件變換設計
- 第四節 Scheduler 線程控制
- 第七章 IOC架構設計
- 第一節 依賴注入與控制反轉
- 第二節 ButterKnife 原理上篇、中篇、下篇
- 第三節 IOC架構設計之Dagger2架構設計
- 第八章 Android架構組件 JetPack
- 第一節 LiveData的工作原理
- 第二節 Navigation 如何解決tabLayout 問題
- 第三節 ViewModel 如何感知View 生命周期及內核原理
- 第四節 Room 架構方式方法
- 第五節 dataBinding 為什么能夠支持MVVM
- 第六節 WorkManager 內核揭秘
- 第七節 Lifecycles 生命周期