在程序開發(fā)中啊研,有個非常重要的思想者祖,《發(fā)現(xiàn)問題眶痰,解決問題》異常捕獲顯然是發(fā)現(xiàn)問題恍飘,解決問題的必要手段之一榨崩,接下來我們先了解下flutter的異常捕獲
Flutter異常捕獲
在介紹Flutter異常捕獲之前必須先了解一下Dart單線程模型,只有了解了Dart的代碼執(zhí)行流程章母,我們才能知道該在什么地方去捕獲異常母蛛。
Dart單線程模型
在Java和Objective-C(以下簡稱“OC”)中,如果程序發(fā)生異常且沒有被捕獲乳怎,那么程序?qū)K止彩郊,但是這在Dart或JavaScript中則不會!究其原因,這和它們的運行機制有關(guān)系秫逝。Java和OC都是多線程模型的編程語言恕出,任意一個線程觸發(fā)異常且該異常未被捕獲時,就會導(dǎo)致整個進程退出违帆。但Dart和JavaScript不會浙巫,它們都是單線程模型,運行機制很相似(但有區(qū)別)刷后,下面我們通過Dart官方提供的一張圖來看看Dart大致運行原理:
Dart 在單線程中是以消息循環(huán)機制來運行的狈醉,其中包含兩個任務(wù)隊列,一個是“微任務(wù)隊列” microtask queue惠险,另一個叫做“事件隊列” event queue。從圖中可以發(fā)現(xiàn)抒线,微任務(wù)隊列的執(zhí)行優(yōu)先級高于事件隊列班巩。
現(xiàn)在我們來介紹一下Dart線程運行過程,如上圖中所示嘶炭,入口函數(shù) main() 執(zhí)行完后抱慌,消息循環(huán)機制便啟動了。首先會按照先進先出的順序逐個執(zhí)行微任務(wù)隊列中的任務(wù)眨猎,事件任務(wù)執(zhí)行完畢后程序便會退出抑进,但是,在事件任務(wù)執(zhí)行的過程中也可以插入新的微任務(wù)和事件任務(wù)睡陪,在這種情況下寺渗,整個線程的執(zhí)行過程便是一直在循環(huán),不會退出兰迫,而Flutter中信殊,主線程的執(zhí)行過程正是如此,永不終止汁果。
在Dart中涡拘,所有的外部事件任務(wù)都在事件隊列中,如IO据德、計時器鳄乏、點擊、以及繪制事件等棘利,而微任務(wù)通常來源于Dart內(nèi)部橱野,并且微任務(wù)非常少,之所以如此赡译,是因為微任務(wù)隊列優(yōu)先級高仲吏,如果微任務(wù)太多,執(zhí)行時間總和就越久,事件隊列任務(wù)的延遲也就越久裹唆,對于GUI應(yīng)用來說最直觀的表現(xiàn)就是比較卡誓斥,所以必須得保證微任務(wù)隊列不會太長。值得注意的是许帐,我們可以通過Future.microtask(…)方法向微任務(wù)隊列插入一個任務(wù)劳坑。
在事件循環(huán)中,當(dāng)某個任務(wù)發(fā)生異常并沒有被捕獲時成畦,程序并不會退出距芬,而直接導(dǎo)致的結(jié)果是當(dāng)前任務(wù)的后續(xù)代碼就不會被執(zhí)行了,也就是說一個任務(wù)中的異常是不會影響其它任務(wù)執(zhí)行的循帐。
Flutter異常捕獲
Dart中可以通過try/catch/finally來捕獲代碼塊異常框仔,這個和其它編程語言類似,如果讀者不清楚拄养,可以查看Dart語言文檔离斩,不再贅述,下面我們看看Flutter中的異常捕獲瘪匿。
Flutter框架異常捕獲
Flutter 框架為我們在很多關(guān)鍵的方法進行了異常捕獲跛梗。這里舉一個例子,當(dāng)我們布局發(fā)生越界或不合規(guī)范時棋弥,F(xiàn)lutter就會自動彈出一個錯誤界面核偿,這是因為Flutter已經(jīng)在執(zhí)行build方法時添加了異常捕獲,最終的源碼如下:
@override
void performRebuild() {
...
try {
//執(zhí)行build方法
built = build();
} catch (e, stack) {
// 有異常時則彈出錯誤提示
built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
}
...
}
可以看到顽染,在發(fā)生異常時漾岳,F(xiàn)lutter默認(rèn)的處理方式是彈一個ErrorWidget,但如果我們想自己捕獲異常并上報到報警平臺的話應(yīng)該怎么做粉寞?我們進入_debugReportException()方法看看:
FlutterErrorDetails _debugReportException(
String context,
dynamic exception,
StackTrace stack, {
InformationCollector informationCollector
}) {
//構(gòu)建錯誤詳情對象
final FlutterErrorDetails details = FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: context,
informationCollector: informationCollector,
);
//報告錯誤
FlutterError.reportError(details);
return details;
}
我們發(fā)現(xiàn)蝗羊,錯誤是通過FlutterError.reportError方法上報的,繼續(xù)跟蹤:
static void reportError(FlutterErrorDetails details) {
...
if (onError != null)
onError(details); //調(diào)用了onError回調(diào)
}
我們發(fā)現(xiàn)onError是FlutterError的一個靜態(tài)屬性仁锯,它有一個默認(rèn)的處理方法 dumpErrorToConsole耀找,到這里就清晰了,如果我們想自己上報異常业崖,只需要提供一個自定義的錯誤處理回調(diào)即可野芒,如:
void main() {
FlutterError.onError = (FlutterErrorDetails details) {
reportError(details);
};
...
}
這樣我們就可以處理那些Flutter為我們捕獲的異常了,接下來我們看看如何捕獲其它異常双炕。
在項目中比較簡單且有效的用法
Future requestApi() async{
try{
// TODO:需要檢查的異常代碼
...
}catch(e){
// debugger 項目中非常好用狞悲,出現(xiàn)異地直接定位到問題的地方,有奇效妇斤,強力推薦讀者自己試一下debugger()
debugger(when: e!= null);
print(e);
}
}
本文 部分代碼來源于《Flutter實戰(zhàn)》
《Flutter實戰(zhàn)》比較實用的一本書摇锋,值得入坑購買(https://u.jd.com/7B1kD2)