欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android穩(wěn)定性:可遠(yuǎn)程配置化的Looper兜底框架

 更新時(shí)間:2023年02月12日 11:46:26   作者:鄒阿濤濤濤濤濤濤  
這篇文章主要為大家介紹了Android穩(wěn)定性可遠(yuǎn)程配置化的Looper兜底框架實(shí)例實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

App Crash對(duì)于用戶(hù)來(lái)講是一種最糟糕的體驗(yàn),它會(huì)導(dǎo)致流程中斷、app口碑變差、app卸載、用戶(hù)流失、訂單流失等。相關(guān)數(shù)據(jù)顯示,當(dāng)Android App的崩潰率超過(guò)0.4%的時(shí)候,活躍用戶(hù)有明顯下降態(tài)勢(shì)。

目前受益于我司采取的一系列的治理、監(jiān)控、防劣化體系,java crash率降低到了一個(gè)十萬(wàn)分級(jí)別的數(shù)字**,**今天分享的就是穩(wěn)定性治理過(guò)程中的一個(gè)重要工具,下面開(kāi)整。

1. 為什么拋出異常時(shí)app會(huì)退出

不細(xì)致分析了,網(wǎng)上隨便找一下就是一堆博客,簡(jiǎn)單來(lái)說(shuō)就是沒(méi)有被catch的崩潰拋出時(shí),會(huì)調(diào)用 Thread#dispatchUncaughtException(throwable) 來(lái)進(jìn)行處理,而在進(jìn)程初始化時(shí),RuntimeInit#commonInit 里會(huì)注入默認(rèn)殺進(jìn)程的 KillApplicationHandler,如果我們沒(méi)有實(shí)現(xiàn)自定義的 UncaughtExceptionHandler 時(shí),dispatchUncaughtException 被調(diào)用就會(huì)走到 KillApplicationHandler 里,把當(dāng)前的進(jìn)程殺掉,即產(chǎn)生了一次用戶(hù)感知的崩潰行為。

2. 有沒(méi)有辦法打造一個(gè)永不崩潰的app

這個(gè)問(wèn)題問(wèn)出來(lái)的前提是指發(fā)生的崩潰是來(lái)自于 java 層面的未捕獲的異常,c 層就是另一回事了。我們來(lái)嘗試回答一下這個(gè)問(wèn)題:

答:可以,至少可以做到把所有的異常都吃掉。

問(wèn):那么怎么做呢?

答:當(dāng)某個(gè)線(xiàn)程發(fā)生異常時(shí),只要不讓 KillApplicationHandler 處理這個(gè)異常就行了,即只要覆蓋掉默認(rèn)的 UncaughtExceptionHandler 就行了噢。

問(wèn):那當(dāng)這樣做的時(shí)候,比如主線(xiàn)程拋出一個(gè)異常被吃掉了,app還能正常運(yùn)行嗎?

答:不能了,因?yàn)椴蛔鋈魏翁幚淼脑?huà),當(dāng)前線(xiàn)程的 Looper.loop()就被終止了。如果是主線(xiàn)程的話(huà),此時(shí)你將會(huì)獲得一個(gè) anr。

問(wèn):怎么才能在吃掉異常的同時(shí),讓主線(xiàn)程繼續(xù)運(yùn)行呢?

答:當(dāng)由于異常拋出,導(dǎo)致線(xiàn)程的 Looper.loop() 終止之后,接管 Looper.loop()。代碼大概長(zhǎng)下面這樣:

public class AppCrashHandler implements UncaughtExceptionHandler {
    @Override
    public void uncaughtException(@NonNull Thread thread, @NonNull Throwable ex) {
        while (true) {
            try {
                if (Looper.myLooper() == null) {
                    Looper.prepare();
                }
                Looper.loop();
            } catch (Exception e) {
            }
        }
    }
}

上面這段代碼,就是我標(biāo)題中被描述為 Looper 兜底框架的實(shí)現(xiàn)機(jī)制。但是對(duì)于一個(gè)正常的app,線(xiàn)上是不可能這樣無(wú)腦的catch,然后 Looper.loop的,這是因?yàn)椋?/p>

  • 不是所有的異常都需要被catch住,如:OOM、launcher Activity onCreate之類(lèi)的。
  • 穩(wěn)定性不是靠屏蔽問(wèn)題,而是靠解決問(wèn)題,當(dāng)異常無(wú)法解決或者解決成本太高,且異常被屏蔽對(duì)用戶(hù)、業(yè)務(wù)來(lái)說(shuō)并沒(méi)有啥實(shí)質(zhì)性的影響時(shí),可以被屏蔽,當(dāng)異常拋出時(shí)已經(jīng)對(duì)業(yè)務(wù)產(chǎn)生了破壞,但是通過(guò)保護(hù)住然后重試可以讓業(yè)務(wù)恢復(fù)運(yùn)作時(shí),也可以被屏蔽,只是多了個(gè)環(huán)節(jié),即修復(fù)異常。

問(wèn):到底什么異常需要被吃掉呢?

上一個(gè)回答中我們大致將需要被吃掉的異常分了兩類(lèi)

  • 異常我們無(wú)法解決或者解決成本太高

舉個(gè)例子,假如公司有使用 react native 之類(lèi)的三方大框架,當(dāng)業(yè)務(wù)拋出來(lái)一個(gè)如下的異常時(shí),我們就可以認(rèn)為這無(wú)法解決。

com.facebook.react.bridge.JSApplicationIllegalArgumentException: connectAnimatedNodes: Animated node with tag (child) [30843] does not exist
    at com.facebook.react.animated.NativeAnimatedNodesManager.connectAnimatedNodes(NativeAnimatedNodesManager.java:7)
    at com.facebook.react.animated.NativeAnimatedModule$16.execute
    at com.facebook.react.animated.NativeAnimatedModule$ConcurrentOperationQueue.executeBatch(NativeAnimatedModule.java:7)
    at com.facebook.react.animated.NativeAnimatedModule$3.execute
    at com.facebook.react.uimanager.UIViewOperationQueue$UIBlockOperation.execute
    at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:19)
    at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:10)
    at com.facebook.react.uimanager.UIViewOperationQueue.access$2600
    at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:6)
    at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:1)
    at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:7)
    at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1118)
    at android.view.Choreographer.doCallbacks(Choreographer.java:926)
    at android.view.Choreographer.doFrame(Choreographer.java:854)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1105)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:238)
    at android.os.Looper.loop(Looper.java:379)
    at android.app.ActivityThread.main(ActivityThread.java:9271)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1018)

2. 異常被屏蔽對(duì)用戶(hù)、業(yè)務(wù)來(lái)說(shuō)并沒(méi)有實(shí)質(zhì)性影響

- 比如老生常談的 Android 7.x toast的 BadTokenException 之類(lèi)的系統(tǒng)崩潰,一呢發(fā)生概率非常低,二呢在Android 8上的修復(fù)方式也只是 try-catch 住。

- 一些不影響業(yè)務(wù)、用戶(hù)使用的三方庫(kù)崩潰,比如瞎說(shuō)一個(gè),當(dāng)使用 OkHttp 在請(qǐng)求接口時(shí),內(nèi)部切了個(gè)線(xiàn)程執(zhí)行了個(gè)更新緩存的任務(wù),結(jié)果里面拋出了一個(gè) NPE 。外面沒(méi)法 try-catch ,而且這個(gè)異常拋出時(shí),頂多下次請(qǐng)求不走緩存,實(shí)際上沒(méi)啥太大影響。

3. 異常很?chē)?yán)重,但是吃掉之后通過(guò)修復(fù)運(yùn)行環(huán)境能夠讓用戶(hù)所使用的業(yè)務(wù)恢復(fù)正常運(yùn)行

比如我們想要保存一張圖片到磁盤(pán)上,但是磁盤(pán)滿(mǎn)了, 拋出了一個(gè)no space left,這時(shí)候我們就可以將異常吃掉,同時(shí)清空app的磁盤(pán)緩存,并且告知用戶(hù)重試,就可以成功的讓用戶(hù)保存圖片成功

3. 如何Looper兜底框架輔助穩(wěn)定性治理

我們先明確一下什么崩潰需要通過(guò)這種手段來(lái)治理、兜底:

系統(tǒng)崩潰,如老生常談的 Android 7.x  toast的 BadTokenException

三方庫(kù)的無(wú)痛崩潰,比如公司有使用 react native 之類(lèi)的三方大框架,沒(méi)有能力改或者不想改一些相關(guān)的 ui 引起的 崩潰,比如做動(dòng)畫(huà)時(shí)莫名其妙的拋出異常

 一些特殊崩潰,如磁盤(pán)空間不足引發(fā)的 no space left,可以嘗試通過(guò)抓住崩潰同時(shí)清理一波app的磁盤(pán)緩存,再?lài)L試?yán)^續(xù)運(yùn)行。

其他...

問(wèn):為什么我的標(biāo)題中強(qiáng)調(diào)了可遠(yuǎn)程配置化呢?

答:因?yàn)榭蛇h(yuǎn)程配置化能夠?yàn)榭蚣鼙旧碣x能更多。

問(wèn):比如?

答:可以提供一種簡(jiǎn)易的線(xiàn)上容災(zāi)機(jī)制,假如線(xiàn)上在某個(gè)頁(yè)面發(fā)生了一個(gè)崩潰,這個(gè)崩潰突然發(fā)生而且崩潰發(fā)生的點(diǎn)本身對(duì)業(yè)務(wù)來(lái)說(shuō)無(wú)關(guān)緊要(比如有個(gè)開(kāi)發(fā)手xx,Integer.parse整了個(gè)漢字,拋異常了),通過(guò)熱修復(fù)來(lái)修吧,流程復(fù)雜,要改代碼、打補(bǔ)丁包、配補(bǔ)丁包。緊急發(fā)版吧,成本比熱修高了不知多少倍,這時(shí)如果有一個(gè)可配置化的Looper兜底框架,我通過(guò)更新我的配置,保護(hù)住這個(gè) Integer.parse 異常,就能很輕松的解決線(xiàn)上問(wèn)題。

4. 可配置化配置的是什么東西

首先這是一個(gè)崩潰保護(hù)的框架,那么配置的肯定是能描述崩潰的內(nèi)容,那么什么東西能描述一個(gè)崩潰呢?無(wú)非就是以下元素:

  • throwable class name
  • throwable message
  • throwable stacktrace
  • Android version
  • app version
  • model
  • brand
  • ...

大致就是對(duì)崩潰做個(gè)標(biāo)簽匹配:這是個(gè)什么崩潰,發(fā)生在哪個(gè)Android版本,發(fā)生在哪個(gè)App版本,發(fā)生在哪個(gè)廠商哪個(gè)系統(tǒng)版本上。

5. 我們?cè)趺醋龅?/h3>

我們的畫(huà)像標(biāo)簽大致長(zhǎng)下面這樣:

[  {    "class": "",    "message": "No space left on device",    "stack": [],
    "app_version": [],
    "clear_cache": 1,
    "finish_page": 0,
    "toast": "",
    "os_version": [],
    "model": []
  },
  {
    "class": "BadTokenException",
    "message": "",
    "stack": [],
    "app_version": [],
    "clear_cache": 0,
    "finish_page": 0,
    "toast": "",
    "os_version": [],
    "model": []
  }
]

配置里還加了一些額外的東西,比如:

  • 崩潰被保護(hù)住的時(shí)候,要不要清理下app的緩存
  • 崩潰被保護(hù)住的時(shí)候,要不要彈個(gè) toast 告知用戶(hù)
  • 崩潰被保護(hù)住的時(shí)候,要不要退出當(dāng)前頁(yè)面

就這樣,我們的可配置化的Looper兜底框架的全貌就描述完了,最后再總結(jié)一下具體的工作流程吧。

Looper兜底流程

我們會(huì)注入自己的 UncaughtExceptionHandler,當(dāng)App產(chǎn)生了一個(gè)未捕獲的異常時(shí),我們通過(guò)對(duì)這個(gè)異常進(jìn)行幾個(gè)標(biāo)簽的匹配來(lái)判斷當(dāng)前的崩潰是否要進(jìn)行保護(hù),當(dāng)需要保護(hù)時(shí),接管Looper.loop,讓線(xiàn)程繼續(xù)運(yùn)行。

配置更新、生效流程:

當(dāng)App啟動(dòng)時(shí),拉取遠(yuǎn)程的崩潰畫(huà)像配置,當(dāng)未捕獲的異常發(fā)生時(shí),讀取本地最新的配置,進(jìn)行標(biāo)簽匹配,如果標(biāo)簽匹配成功,進(jìn)行Looper兜底。

以上就是Android穩(wěn)定性:可遠(yuǎn)程配置化的Looper兜底框架的詳細(xì)內(nèi)容,更多關(guān)于Android遠(yuǎn)程配置化Looper框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論