Android app會crash的原因及解決方法
android main入口的commonInit()方法內處,有這麼一句話,
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
如果沒有這句話,app就不會crash。不信,你往裡面看,
public KillApplicationHandler(LoggingHandler loggingHandler) { @Override public void uncaughtException(Thread t,Throwable e) { //捕獲到異常 try { ...... //列印crash日誌,展示崩潰彈窗等 // Bring up crash dialog,wait for it to be dismissed ActivityManager.getService().handleApplicationCrash( mApplicationObject,new ApplicationErrorReport.ParcelableCrashInfo(e)); } catch (Throwable t2) { .... } finally { // Try everything to make sure this process goes away. Process.killProcess(Process.myPid());//殺死程序 System.exit(10); } } }
當異常KillApplicationHandler捕獲到異常,進行完一系列處理(主要是列印crash日誌,通知AMS展示crash彈窗等)後,最終會殺死程序,這樣你的app就崩潰了。
既然都崩潰了,自定義異常捕獲器來遮蔽crash真的可行嗎?
肯定有人會說,自定義一個異常捕獲器,來覆蓋掉系統的KillApplicationHandler,然後在捕獲到異常後,不殺程序,app就不會崩潰了,就像下面這樣,
class MainApplication : Application() { override fun onCreate() { super.onCreate() Thread.setDefaultUncaughtExceptionHandler { _,e -> //捕獲到異常,只打印日誌,不殺程序 Log.e("MainApplication","${Thread.currentThread().name} 捕獲到異常:${e.message}") } } }
這其實只是隔壁老王的思路,雖然確實防護住子執行緒的crash,但是當主執行緒出現異常時,app還是無法正常執行。這是因為,當UncaughtExceptionHandler捕獲到執行緒丟擲異常的時候,執行緒在執行完uncaughtException()中的處理後,就無法繼續存活了。如果拋異常的執行緒是主執行緒,那就意味著主執行緒會死掉,這時你即便不殺程序,程序活著也沒有任何意義了,app還是會停止執行。
把android異常捕獲機制在梳理一下,熟悉的同學可以跳過,直接進入下一節。
- Thread.setCaughtExceptionPreHandler()覆蓋所有執行緒,會在回撥DefaultExceptionHandler之前呼叫;
- Thread.setCaughtExceptionHandler()同樣回覆蓋所有執行緒,可以在應用層被重複呼叫,並且每一次呼叫後,都會覆蓋上一次設定的DefaultUncaughtExceptionHandler;
- Thread.currentThread.setUncaughtExceptionHandler(),只可以覆蓋當前執行緒的異常。如果某個執行緒存在自定義的UncaughtExceptionHandler,回撥時會忽略全域性的DefaultUncaughtHandler。
既然話都說到這份上了,就請接下never crash大招吧。
要想不crash,只能讓執行緒不要丟擲exception,唯此別無他法。如果我們能把一個執行緒的所有的操作都使用try-catch進行保護,理論上,就能做到app never crash。由於android基於Handler事件驅動的機制,可以在app啟動時,向主執行緒中的MessageQueue中提交一個死迴圈操作,在這個死迴圈中不斷去poll事件,並且將這個死迴圈進行try-catch,這樣所有主執行緒中的異常都會被catch住,從而app就再也不會發生crash。
private fun openCrashProtected() { Log.d(tag,"openCrashProtected") Handler(Looper.getMainLooper()).post { while (true) { try { Looper.loop() Log.d(tag,"main looper execute loop") } catch (e: Throwable) { //所有主執行緒中的異常都會被catch住,從而不會發生crash Log.e(tag,"catch exception: " + e.message) } } } }
有人可能要說了,你這樣catch住主執行緒的異常了,頁面可能要亂套哇。話雖如此,但你可以在catch中做業務保護呀。比如,我這裡採取的做法是,關閉棧頂activity。 解決ActivityLifeCycle,維護一個Activity棧,
private fun registerLifeCycle() { registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { override fun onActivityCreated(activity: Activity,savedInstanceState: Bundle?) { ActivityStack.Instance().push(activity) } override fun onActivityResumed(activity: Activity) { } override fun onActivityStarted(activity: Activity) { } override fun onActivityPaused(activity: Activity) { } override fun onActivityDestroyed(activity: Activity) { ActivityStack.Instance().pop(activity) } override fun onActivitySaveInstanceState(activity: Activity,outState: Bundle) { } override fun onActivityStopped(activity: Activity) { } }) }
然後當catch住異常時,
//主執行緒出現異常,關閉棧頂activity ActivityStack.Instance().curr()?.finish()
github程式碼
最後奉上github倉庫程式碼,請笑納。
以上就是Android app會crash的原因及解決方法的詳細內容,更多關於Android app crash的資料請關注我們其它相關文章!