近被微信一個ANR的bug在網(wǎng)上刷的鋪天蓋地,那么什么是ANR呢?
ANR(Application Not Responding),因為Android的主線程用來處理UI等邏輯,如果主線程進行耗時操作而阻塞,就會導(dǎo)致UI上的事件無響應(yīng)的卡死,這個時候超過一定的時間后(Activity的長執(zhí)行時間是5秒,BroadcastReceiver的長執(zhí)行時間則是10秒),便會彈出Dialog詢問關(guān)閉還是等待,如下圖。
近日,有網(wǎng)友表示,在微信中輸入“兩個數(shù)字+15/20 個中文字符的句號”(另一說法是任意數(shù)字,任意 15 個標(biāo)點),部分 Android 手機發(fā)送或者收到該消息時,微信就會無響應(yīng)。
案例 1:15。。。。。。。。。。。。。。。
案例 2:15。。。。。。。。。。。。。。。。。。。。
以下為主流手機的表現(xiàn)
小米 6:沒問題(但將句號改為 20 個時,手機卡死);
蘋果:沒問題;
mete 9:卡死;
三星:卡死;
360 手機:卡死。
那么這個問題是怎么產(chǎn)生的呢?
首先,微信發(fā)生ANR以后,會生成traces.txt文件。通過adb 導(dǎo)出
adb pull /data/anr/traces.txt ~/
其中有這么一段:
native: #05 pc 0043a419 /data/dalvik-cache/arm/system@framework@boot.oat (Java_java_util_regex_Matcher_setInputImpl__JLjava_lang_String_2II+132)
at java.util.regex.Matcher.setInputImpl(Native method)
at java.util.regex.Matcher.resetForInput(Matcher.java:252)
- locked <0x0ecefa84> (a java.util.regex.Matcher)
at java.util.regex.Matcher.reset(Matcher.java:208)
at java.util.regex.Matcher.reset(Matcher.java:177)
at java.util.regex.Matcher.(Matcher.java:90)
at java.util.regex.Pattern.matcher(Pattern.java:297)
at com.tencent.mm.ui.widget.celltextview.g.a.o(SourceFile:95)
at com.tencent.mm.ui.widget.celltextview.g.a.dc(SourceFile:55)
at com.tencent.mm.ui.widget.celltextview.f.b.a(SourceFile:76)
at com.tencent.mm.ui.widget.celltextview.d.a.Cw(SourceFile:466)
at com.tencent.mm.ui.widget.celltextview.d.a.Cp(SourceFile:92)
at com.tencent.mm.ui.widget.celltextview.CellTextView.onMeasure(SourceFile:102)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
發(fā)現(xiàn)是cellTextView鎖在了celltextView正則的時候。
于是乎debug celltextview包的a類的o方法,
發(fā)現(xiàn)一段超級復(fù)雜的正則(部分位置打碼),所以初步斷定為可能是正則時間太長導(dǎo)致。于是寫了一個單元測試,來測試該正則是否有問題。
實驗發(fā)現(xiàn),這個正則根本不會導(dǎo)致耗時過長,平均耗時0-1ms。
那也就是說明,其實不是這里的原因。
于是將斷點打靠上層,到 com.tencent.mm.ui.widget.celltextview.f.b.a() 方法上
點擊放過按鈕發(fā)現(xiàn)程序無限次落到這個斷點上,由此可知,是造成了死循環(huán),無限調(diào)用a()方法導(dǎo)致的。
繼續(xù)深究,為什么會導(dǎo)致死循環(huán)。
線索1:
發(fā)現(xiàn)a()方法上面有一個判斷,會導(dǎo)致跳到cond_6終會繼續(xù)跳到goto_4調(diào)用a()方法。
這里有個
add-int/lit8 v4, v4, -0x1
其實他相當(dāng)于
i-1
線索2
觀察a()方法后面,有wwk,width等屬性調(diào)用。
結(jié)合線索
接下來,打開jadx,將class文件反編譯為java文件,利用線索快速定位代碼。發(fā)現(xiàn)這些邏輯代碼片段如下:
下面來看java代碼。
可以看到有兩個while循環(huán),這里不關(guān)心外部while,因為可以看出真正卡死的是在內(nèi)部while循環(huán)。
內(nèi)部while循環(huán)首先判斷了dVar2 是否為空,以及dVar2的text是否為空。
debug發(fā)現(xiàn),dVar2是一個TextPaint類,用于繪制文本信息(包括字號,大小,顏色,超鏈接樣式之類的)。
也就是說,只要dVar2不為空,這個循環(huán)就不會退出,根據(jù)代碼可以看出,只有在i4>0的時候才可能把dVar2置為空:
那么i4是什么呢,在紅框上面可以看到,i4是a的wwk屬性。這個值暫時不知道是什么。
不過通過debug發(fā)現(xiàn),這個wwk是始終等于0的,也就是不滿足while內(nèi)部的dVar2的置空條件,也就造成了while死循環(huán)。
于是乎,造成anr的根本原因就是在這個while里了。
關(guān)于這個bug在知乎上騰訊的工程師表示已經(jīng)開始著手修復(fù)了,這個bug主要還是因為微信增加了對文字對齊的重新排版導(dǎo)致的,也算是在改進的道路上遇到的一點點小問題吧,個人覺得還是蠻有意思的。
熱點新聞
課程問答