Android app會(huì)crash的原因及解決方法
android main入口的commonInit()方法內(nèi)處,有這么一句話,
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
如果沒有這句話,app就不會(huì)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());//殺死進(jìn)程
System.exit(10);
}
}
}
當(dāng)異常KillApplicationHandler捕獲到異常,進(jìn)行完一系列處理(主要是打印crash日志,通知AMS展示crash彈窗等)后,最終會(huì)殺死進(jìn)程,這樣你的app就崩潰了。
既然都崩潰了,自定義異常捕獲器來屏蔽crash真的可行嗎?
肯定有人會(huì)說,自定義一個(gè)異常捕獲器,來覆蓋掉系統(tǒng)的KillApplicationHandler,然后在捕獲到異常后,不殺進(jìn)程,app就不會(huì)崩潰了,就像下面這樣,
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler { _, e ->
//捕獲到異常,只打印日志,不殺進(jìn)程
Log.e("MainApplication", "${Thread.currentThread().name} 捕獲到異常:${e.message}")
}
}
}
這其實(shí)只是隔壁老王的思路,雖然確實(shí)防護(hù)住子線程的crash,但是當(dāng)主線程出現(xiàn)異常時(shí),app還是無(wú)法正常運(yùn)行。這是因?yàn)椋?dāng)UncaughtExceptionHandler捕獲到線程拋出異常的時(shí)候,線程在執(zhí)行完uncaughtException()中的處理后,就無(wú)法繼續(xù)存活了。如果拋異常的線程是主線程,那就意味著主線程會(huì)死掉,這時(shí)你即便不殺進(jìn)程,進(jìn)程活著也沒有任何意義了,app還是會(huì)停止運(yùn)行。
把a(bǔ)ndroid異常捕獲機(jī)制在梳理一下,熟悉的同學(xué)可以跳過,直接進(jìn)入下一節(jié)。
- Thread.setCaughtExceptionPreHandler()覆蓋所有線程,會(huì)在回調(diào)DefaultExceptionHandler之前調(diào)用;
- Thread.setCaughtExceptionHandler()同樣回覆蓋所有線程,可以在應(yīng)用層被重復(fù)調(diào)用,并且每一次調(diào)用后,都會(huì)覆蓋上一次設(shè)置的DefaultUncaughtExceptionHandler;
- Thread.currentThread.setUncaughtExceptionHandler(),只可以覆蓋當(dāng)前線程的異常。如果某個(gè)線程存在自定義的UncaughtExceptionHandler,回調(diào)時(shí)會(huì)忽略全局的DefaultUncaughtHandler。
既然話都說到這份上了,就請(qǐng)接下never crash大招吧。
要想不crash,只能讓線程不要拋出exception,唯此別無(wú)他法。如果我們能把一個(gè)線程的所有的操作都使用try-catch進(jìn)行保護(hù),理論上,就能做到app never crash。由于android基于Handler事件驅(qū)動(dòng)的機(jī)制,可以在app啟動(dòng)時(shí),向主線程中的MessageQueue中提交一個(gè)死循環(huán)操作,在這個(gè)死循環(huán)中不斷去poll事件,并且將這個(gè)死循環(huán)進(jìn)行try-catch,這樣所有主線程中的異常都會(huì)被catch住,從而app就再也不會(huì)發(fā)生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) {
//所有主線程中的異常都會(huì)被catch住,從而不會(huì)發(fā)生crash
Log.e(tag, "catch exception: " + e.message)
}
}
}
}
有人可能要說了,你這樣catch住主線程的異常了,頁(yè)面可能要亂套哇。話雖如此,但你可以在catch中做業(yè)務(wù)保護(hù)呀。比如,我這里采取的做法是,關(guān)閉棧頂activity。 解決ActivityLifeCycle,維護(hù)一個(gè)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) {
}
})
}
然后當(dāng)catch住異常時(shí),
//主線程出現(xiàn)異常,關(guān)閉棧頂activity ActivityStack.Instance().curr()?.finish()
github代碼
最后奉上github倉(cāng)庫(kù)代碼,請(qǐng)笑納。
以上就是Android app會(huì)crash的原因及解決方法的詳細(xì)內(nèi)容,更多關(guān)于Android app crash的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
另外兩種Android沉浸式狀態(tài)欄實(shí)現(xiàn)思路
這篇文章主要為大家介紹了另外兩種Android沉浸式狀態(tài)欄實(shí)現(xiàn)思路,android5.0及以后版本都支持給狀態(tài)欄著色,而目前android主流版本還是4.4,想要深入了解的朋友可以參考一下2016-01-01
Flutter版本的自定義短信驗(yàn)證碼實(shí)現(xiàn)示例解析
這篇文章主要介紹了Flutter版本的自定義短信驗(yàn)證碼實(shí)現(xiàn)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
android實(shí)現(xiàn)音樂跳動(dòng)效果的示例代碼
這篇文章主要介紹了android實(shí)現(xiàn)音樂跳動(dòng)效果的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Android升級(jí)gradle 后引入aar包報(bào)錯(cuò)解決
這篇文章主要為大家介紹了Android升級(jí)gradle 后引入aar包報(bào)錯(cuò)解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Android Init進(jìn)程對(duì)信號(hào)的處理流程詳細(xì)介紹
這篇文章主要介紹了Android Init進(jìn)程對(duì)信號(hào)的處理流程詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-02-02
Android進(jìn)階CoordinatorLayout協(xié)調(diào)者布局實(shí)現(xiàn)吸頂效果
這篇文章主要為大家介紹了Android進(jìn)階CoordinatorLayout協(xié)調(diào)者布局實(shí)現(xiàn)吸頂效果,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
Android列表動(dòng)圖展示的實(shí)現(xiàn)策略
這篇文章主要給大家介紹了關(guān)于Android列表動(dòng)圖展示的實(shí)現(xiàn)策略的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11

