博客地址: dim.red
0x00 前言
分析最近的 Crash , 排在前列基本是第三方SDK導(dǎo)致. 遇到這樣的問題, 我們需要尋求官方的支持, 但是官方的支持總是來(lái)得比較晚. 在Android上, 第三方SDK一般是兩種, 一種是jar, 一種是so. so的修改成本過大, 我們放棄它, 我們現(xiàn)在探討對(duì) jar 的修改.
0x02 問題
在之前 使用AOP來(lái)為第三方SDK打CALL 文章 我們使用 AOP 對(duì)第三方 SDK 的問題進(jìn)行修復(fù). 貌似能這樣解決這個(gè)問題.
但是 AOP 存在局限性:
1、它不夠直觀. 定義的規(guī)則需要通過編譯后才能確定.
2、學(xué)習(xí)成本較高.
3、可操作的范圍不夠大. 只能切方法.
例子:
在一些因素下,我們升級(jí)某個(gè)推送SDK版本. 但是發(fā)現(xiàn)有大量的NPE異常出現(xiàn). 通過反編譯 jar 定位問題.
private Handler p = null;
private void b() {
if (this.p != null) {
this.p.removeMessages(2);
} else {
this.p = new Handler(Looper.getMainLooper(), new com.xxx.b(this));
}
this.p.sendEmptyMessageDelayed(2, 3000L);
}
private void c() {
if (this.p != null) {
this.p.removeMessages(2);
this.p = null;
}
}
public void d(){
...
// 代碼塊A 開始
if(p != null){
this.p.sendEmptyMessageDelayed(3, 3000L);
}
// 代碼塊A 結(jié)束
...
}
異常發(fā)生在方法this.p.sendEmptyMessageDelayed(2, 3000L);.在一個(gè)判斷空還會(huì)出現(xiàn)NPE, 說明這是一個(gè)多線程并發(fā)下的bug. 通過分析我們需要
- 對(duì)b c 方法使用synchronized 修飾
- 對(duì)代碼塊A 進(jìn)行 synchronized(this) 包裹
在這種場(chǎng)景下使用AOP將費(fèi)力不討好.
0x03 解決方案
我們可以嘗試使用一種更簡(jiǎn)單的方式來(lái)處理這件事情. 輸入一個(gè)原始的 jar , 經(jīng)過轉(zhuǎn)換生成新的 jar.
- 步驟1: 對(duì) jar 中需要修改的 class 反編譯成 java 文件.
- 步驟2: 對(duì) java 文件進(jìn)行代碼邏輯上的 bug fix.
- 步驟3: 使用 javac 編譯 java 文件成 class 文件.
- 步驟4: 替換 jar 中對(duì)應(yīng)的修改的 class 文件生成新的 jar 文件.
0x04 問題
Q : javac 編譯失敗.
A: 當(dāng)你 java 調(diào)用的方法不在原有的 jar 中, 導(dǎo)致 javac 編譯的時(shí)候找不到對(duì)應(yīng)的方法,拋出異常.
解決方式:使用 asm 對(duì) jar 中進(jìn)行指令分析, 對(duì)指令 invokestatic, invokevirtual , invokeinterface , invokedynamic ( android 上可以忽略這個(gè)指令) 和 getfield , getstatic 指令的分析. 我們可以生成一個(gè)空的實(shí)現(xiàn)的jar , 來(lái)為 javac 編譯提供環(huán)境支持.
Q : 內(nèi)部類的問題.
A: 匿名內(nèi)部的生成的規(guī)則是 外部類類名$Number. Number 是在源碼中出現(xiàn)的位置. 當(dāng)你新增和調(diào)整位置的時(shí)候會(huì)導(dǎo)致生成的類和之前的類不匹配. 解決方式. 在對(duì)外部類操作的時(shí)候, 直接對(duì)匿名內(nèi)部類進(jìn)行刪除. 等 javac 命令的生成新的內(nèi)部類直接替換進(jìn)去.
0x05 改進(jìn)
通過和 idea 結(jié)合提供一整套的解決方案.