解決SystemUI下拉框中,長按WIFI圖標會導致崩潰問題
文章目錄
- 場景
- 參考資料
- 修改文件
- 解決方案
- 日志
- 源碼分析
- 總結
場景
在部分產品中偶發性發現,
- SystemUI下拉框下拉后長按WIFI圖標會導致崩潰問題,有時候是截屏、點擊Home 按鍵后,長按WIFI圖標崩潰。
- 第一次刷完固件開機OK的,重啟后會復現,或者直接長按 崩潰必現。
這個現象很奇怪,平常自己開發中并不是所有項目都會遇到,
參考資料
Android11 下拉菜單長按WIFI 圖標SystemUI ANR
參考資料有部分完整的報錯信息日志和源碼分析,方便理解問題。 核心就是在Handler 里面創建了Handler,Android體系不允許的。
new Handler()和new Handler(Looper.getMainLooper())的區別是什么?
區別總結來說:
-
主線程本身就有一個Lopper,在程序起來的時候就已經lopper() 了,所以在主線程里面創建Handler,直接 new
Handler()。 這個handler 發送消息自動會在Lopper() 隊列里面等待執行。 -
子線程里面不一樣了,你創建了一個new Handler(), 系統不允許就報錯了。 因為本身這個Handler()
創建了就是個死Handler,無法讓消息轉動起來的。 所以系統不允許就直接報錯了,如果想這樣就是要在子線程中創建Handler ,那么就兩個方案: 讓這個Handler 有自己的Looper,所有就有了Lopper.prepare 和 Looper.loop; 或者在這個主線程中穿件的Handler 和 主線程關聯起來,構造方法傳遞一個MainLoop 不就行了嘛。
如果 必現,這樣的解決方案是OK的,就是在創建Handler 前后加Lopper.prepare() 和 Lopper.loop() 讓線程中的消息循環起來。
如果是偶現,這樣的解決方式肯定是不合理的,下面會給出我的修改解決方案
如果需要深究偶發原因,還需進一步重點分析,問題原因很簡單就是子線程里面跑了Handler.
修改文件
相關關聯文件
/vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
/vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/assist/AssistManager.java修改文件
/vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/assist/AssistManager.java具體修改內容
mAssistDisclosure = new AssistDisclosure(context, new Handler());修改為如下:
mAssistDisclosure = new AssistDisclosure(context, new Handler(Looper.getMainLooper()));
解決方案
日志
這里直接張貼一下別人家的日志 下。
10-01 08:01:11.236 5792 5833 E AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
10-01 08:01:11.236 5792 5833 E AndroidRuntime: Process: com.android.systemui, PID: 5792
10-01 08:01:11.236 5792 5833 E AndroidRuntime: java.lang.RuntimeException: Can't create handler inside thread Thread[AsyncTask #1,5,main] that has not called Looper.prepare()
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at android.os.Handler.<init>(Handler.java:227)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at android.os.Handler.<init>(Handler.java:129)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at com.android.systemui.assist.AssistManager.<init>(AssistManager.java:213)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at com.android.systemui.assist.AssistManager_Factory.provideInstance(AssistManager_Factory.java:107)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at com.android.systemui.assist.AssistManager_Factory.get(AssistManager_Factory.java:70)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at com.android.systemui.assist.AssistManager_Factory.get(AssistManager_Factory.java:17)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at com.android.systemui.statusbar.phone.StatusBar.lambda$startActivityDismissingKeyguard$16(StatusBar.java:2709)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at com.android.systemui.statusbar.phone.StatusBar.lambda$startActivityDismissingKeyguard$16$StatusBar(Unknown Source:0)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at com.android.systemui.statusbar.phone.-$$Lambda$StatusBar$fPMIOsYMhFXVKHESAjUObpcgeJM.run(Unknown Source:10)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
10-01 08:01:11.236 5792 5833 E AndroidRuntime: at java.lang.Thread.run(Thread.java:923)
源碼分析
根據報錯日志,找到了源碼
報錯的地方,注意兩個地方:
- 報錯的地方已經修改,Handler() 構造方法,傳遞一個Looper.getMainLooper() 放到主線程
- 這個類上面有一個Inject 注解,這個注意下,在引用地方就不是new 對象了的。
可以根據實際日志,反推調用地方是哪里報錯了,如下追蹤到StatusBar.java 源碼了。
通過get() 方法,獲取對象,創建了AssistManager 對象, 但是它是放到子線程里面調用的。 然后在AssistManager 構造方法里面穿件了Handler,所以直接報錯了。
總結
通過這里的筆記學習和相關資料參考
- 搞清楚Handler、Loop 的基本原理 相關原理不復雜需要掌握基本知識;搞清楚Looper.loop Lopper.prepare、Loop.getMainLooper 這些方法的使用。