最近想給 App 加上一個崩潰后自動重啟的功能,便去查找了下資料,畢竟有很長一段時間沒弄過,
不搜不知道,一搜嚇一跳,居然看到這庫的實作思路,居然能夠讓 App 產生例外后,不會崩潰,
我當場的表情是這樣的:
學完后,表情是這樣的:
好了,廢話不多說,趕緊進正文,
該庫的 GitHub 地址為:
https://github.com/android-notes/Cockroach
其有兩個版本,兩個版本的思路是不一樣的,但是能夠實作同樣的功能——App 不會崩潰,
在講解它的原理之前,我們還是來簡單復習下,如何在 App 崩潰的時候,捕獲例外進行處理:
UncaughtExceptionHandler
寫個例外捕獲類:
MyUncaughtExceptionHandler.kt:
object MyUncaughtExceptionHandler : Thread.UncaughtExceptionHandler {
//獲取系統默認的例外處理機制
private var defaultUncaughtExceptionHandler: Thread.UncaughtExceptionHandler =
Thread.getDefaultUncaughtExceptionHandler()
override fun uncaughtException(t: Thread, e: Throwable) {
//自己處理
Log.e("uncaughtException TAG", e.message.toString())
// //交給系統默認處理
// defaultUncaughtExceptionHandler.uncaughtException(t, e)
}
}
在自定義 Application 中注冊:
MyApplication.kt:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler(MyUncaughtExceptionHandler)
}
}
在 AndroidManifest.xml 中注冊:
android:name=".MyApplication"
在類里面寫個例外:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
"a".toInt()
}
}
運行,崩潰!
2021-03-31 00:31:10.461 352-352/? E/TAG: For input string: "a"
Cockroach 1.0
它的實作思路其實很簡單,其實就是 try catch 全部代碼就行,
是不是很驚訝?
這要從 Handler 機制講起了,
在ActivityThread.java的main(String[] args)方法中,有這樣一段代碼:
public static void main(String[] args) {
···
Looper.prepareMainLooper();
···
Looper.loop();
···
}
所以的話,主執行緒其實一直在 loop() 方法的死回圈中,至于為什么是死回圈?那就不用說了,必須是死回圈,不是死回圈的話,運行完 main() 方法,整個程式不就執行完了,那程式就被 cut 掉了,
至于在死回圈中是如何啟動 Activity 等操作的,是通過 Handler 發訊息到 MessageQueue 中(這里默認講的都是主執行緒),mainLooper 不斷去 MessageQueue 獲取訊息并執行,所以的話,其實一切主執行緒的操作都是在 loop() 中執行的,既然如此,那么我們對其 try catch 不就行了嗎?
public static void main(String[] args) {
···
Looper.prepareMainLooper();
···
try{
Looper.loop()
}catch (throwable: Throwable){
Log.e("TAG", throwable.message.toString())
}
···
}
為什么是 Throwable?因為 Exception 和 Error 都是繼承 Throwable 的,
但是,這樣還是不行,因為我們不可能去改原始碼,
好,那我們不改原始碼,直接在 MyApplication 中設定:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler(MyUncaughtExceptionHandler)
try{
Looper.loop()
}catch (throwable: Throwable){
Log.e("try catch TAG", throwable.message.toString())
}
}
}
效果一樣!
我們運行下:
2021-03-31 00:56:56.838 774-774/com.bjsdm.handlecrash E/try catch TAG: Unable to start activity ComponentInfo{com.bjsdm.handlecrash/com.bjsdm.handlecrash.MainActivity}: java.lang.NumberFormatException: For input string: "a"
注意看,是try catch TAG,不是uncaughtException TAG,說明是被 try catch 成功了,而非引起 App 崩潰,
為什么?
Looper.loop() 里面是個死回圈,在死回圈里面再呼叫 Looper.loop() ,這樣前一個就無效了,但是功能還是能正常運行,
但是我們還要優化下,畢竟一次例外就跳出 try catch 了,
while (true){
try{
Looper.loop()
}catch (throwable: Throwable){
Log.e("try catch TAG", throwable.message.toString())
}
}
這樣還是不夠,因為一旦呼叫 Looper.loop() 就進入死回圈,這就有可能上一個 Looper.loop() 的訊息還未執行完畢,
Handler(Looper.getMainLooper()).post {
while (true) {
try {
Looper.loop()
} catch (throwable: Throwable) {
Log.e("try catch TAG", throwable.message.toString())
}
}
}
還可以提高一下優先級:
Handler(Looper.getMainLooper()).postAtFrontOfQueue() {
while (true) {
try {
Looper.loop()
} catch (throwable: Throwable) {
Log.e("try catch TAG", throwable.message.toString())
}
}
}
大致思路就是這樣了,這樣在主執行緒產生例外的話,是不會崩潰的,
注意,是主執行緒,所以的話,假如在子執行緒出現例外的話,還是不行,這時候就需要 UncaughtExceptionHandler 了,
當然,我也有一個笨的方法:
因為一切子執行緒的運行基本都是基于 Runnable 介面的,要不,我們 try catch 它的 run 方法?
可以考慮位元組碼插樁,不懂位元組碼插樁可以看看這篇文章自定義Gradle Plugin+位元組碼插樁
當然也可以考慮繼承 Runnable 介面,后續使用 Runnable 時,使用封裝的那個:
abstract class CaughtExceptionRunnable : Runnable {
override fun run() {
try {
myRun();
} catch (throwable: Throwable) {
Log.e(" run try catch TAG", throwable.message.toString())
}
}
abstract fun myRun()
}
Thread(object : CaughtExceptionRunnable() {
override fun myRun() {
"a".toInt()
}
}).start()
2021-03-31 01:32:22.081 1137-1152/com.bjsdm.handlecrash E/ run try catch TAG: For input string: "a"
Cockroach 2.0
關于這個我就不多說了,因它的原理是通過反射獲取主執行緒的 Handler,然后攔截它的生命周期的訊息,然后進行 try catch,這樣能夠更精準地處理例外,但是,隨著版本更改,反射的限制會越來越強,另外,生命周期的標識也可以會更改,所以的話,理清思路就行了,
em…
該庫類目前可以取消反射限制
https://github.com/tiann/FreeReflection
這是我的公眾號,關注獲取第一資訊!!歡迎關注支持下,謝謝!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/271600.html
標籤:其他
上一篇:android 多屏異屏顯
