1斋荞、Dart 單線程模型
在 Java 和 Objective-C(以下簡(jiǎn)稱 OC
)中,如果程序發(fā)生異常且沒(méi)有被捕獲,那么程序?qū)?huì)終止床绪,但是這在 Dart 或 JavaScript 中則不會(huì)机杜。這是因?yàn)?Java 和 OC 都是多線程模型的編程語(yǔ)言帜讲,任意一個(gè)線程觸發(fā)異常且該異常未被捕獲時(shí),就會(huì)導(dǎo)致整個(gè)進(jìn)程退出椒拗。而 Dart 和 JavaScript 都是單線程模型似将。
解析:
1获黔、Dart 在單線程中是以消息循環(huán)機(jī)制
來(lái)運(yùn)行的。其包含兩個(gè)隊(duì)列在验,一個(gè)是微任務(wù)隊(duì)列(microtask queue)
玷氏,另一個(gè)是事件隊(duì)列(event queue)
。并且微任務(wù)隊(duì)列的執(zhí)行優(yōu)先級(jí)高于事件隊(duì)列腋舌。
2盏触、Dart 線程的運(yùn)行,在入口函數(shù) main() 執(zhí)行完后块饺,消息循環(huán)機(jī)制便啟動(dòng)了赞辩。首先會(huì)按照先進(jìn)先出
的順序逐個(gè)執(zhí)行微任務(wù)隊(duì)列(microtask queue)中的任務(wù),然后是事件隊(duì)列(event queue)中的任務(wù)授艰,待事件隊(duì)列中的任務(wù)執(zhí)行完畢后程序便會(huì)退出辨嗽。但是,在事件任務(wù)執(zhí)行的過(guò)程中也可以插入新的微任務(wù)和事件任務(wù)淮腾,在這種情況下糟需,整個(gè)線程的執(zhí)行過(guò)程便是一直在循環(huán),不會(huì)退出来破,而 Flutter 中篮灼,主線程的執(zhí)行過(guò)程正是如此,永不終止徘禁。
3诅诱、在 Dart 中,所有的外部事件任務(wù)都在事件隊(duì)列中
送朱,如 IO娘荡、計(jì)時(shí)器、點(diǎn)擊驶沼、以及繪制事件等炮沐。而微任務(wù)通常來(lái)源于 Dart 內(nèi)部
,并且微任務(wù)非常少回怜,之所以如此大年,是因?yàn)槲⑷蝿?wù)隊(duì)列優(yōu)先級(jí)高,如果微任務(wù)太多玉雾,執(zhí)行時(shí)間總和就越久翔试,事件隊(duì)列任務(wù)的延遲也就越久,對(duì)于 GUI 應(yīng)用來(lái)說(shuō)最直觀的表現(xiàn)就是比較卡复旬,所以必須得保證微任務(wù)隊(duì)列不會(huì)太長(zhǎng)垦缅。注意注意,可以通過(guò)Future.microtask(…)
方法向微任務(wù)隊(duì)列插入一個(gè)任務(wù)驹碍。
4壁涎、在事件循環(huán)中凡恍,當(dāng)某個(gè)任務(wù)發(fā)生異常并沒(méi)有被捕獲時(shí),程序并不會(huì)退出怔球,而直接導(dǎo)致的結(jié)果是當(dāng)前任務(wù)
的后續(xù)代碼就不會(huì)被執(zhí)行了嚼酝,也就是說(shuō)一個(gè)任務(wù)中的異常是不會(huì)影響其他任務(wù)執(zhí)行的。
5庞溜、Dart 中也可以通過(guò) try/catch/finally 來(lái)捕獲代碼塊異常革半。
2碑定、Flutter 中的異常捕獲
1流码、Flutter 框架異常捕獲
Flutter 框架在很多關(guān)鍵的方法進(jìn)行了異常捕獲。如:布局發(fā)生越界或不合規(guī)范時(shí)延刘,F(xiàn)lutter 就會(huì)自動(dòng)彈出一個(gè)錯(cuò)誤界面(ErrorWidget)漫试,這是因?yàn)?Flutter 已經(jīng)在執(zhí)行 build 方法時(shí)添加了異常捕獲。其源碼如下:
@override
void performRebuild() {
...
try {
//執(zhí)行build方法
built = build();
} catch (e, stack) {
// 有異常時(shí)則彈出錯(cuò)誤提示
built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
}
...
}
異常上報(bào)
通過(guò) FlutterError 中一個(gè)靜態(tài)屬性 onError 的一個(gè)默認(rèn)處理方法 dumpErrorToConsole 來(lái)實(shí)現(xiàn)異常上報(bào)碘赖。
void main() {
FlutterError.onError = (FlutterErrorDetails details) {
reportError(details);
};
...
}
2驾荣、其他異常捕獲與日志收集
在 Flutter 中,有一些 Flutter 沒(méi)有捕獲的異常普泡,如:調(diào)用空對(duì)象方法異常播掷、Future 中的異常。在 Dart 中撼班,異常分為同步異常和異步異常歧匈,同步異常可以通過(guò) try/catch 捕獲砰嘁;而異步異常則比較麻煩件炉,如下面的代碼是捕獲不了 Future 異常的:
try{
Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
}catch (e){
print(e)
}
可以通過(guò) onError
+ runZoned(...)
方法來(lái)實(shí)現(xiàn)異常捕獲和日志上報(bào):
void collectLog(String line){
//收集日志
}
void reportErrorAndLog(FlutterErrorDetails details){
//上報(bào)錯(cuò)誤和日志邏輯
}
FlutterErrorDetails makeDetails(Object obj, StackTrace stack){
// 構(gòu)建錯(cuò)誤信息
}
void main() {
var onError = FlutterError.onError; //先將 onerror 保存起來(lái)
FlutterError.onError = (FlutterErrorDetails details) {
onError?.call(details); //調(diào)用默認(rèn)的onError
reportErrorAndLog(details); //上報(bào)
};
runZoned(
() => runApp(MyApp()),
zoneSpecification: ZoneSpecification(
// 攔截print
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
collectLog(line);
parent.print(zone, "Interceptor: $line");
},
// 攔截未處理的異步錯(cuò)誤
handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone,
Object error, StackTrace stackTrace) {
reportErrorAndLog(details);
parent.print(zone, '${error.toString()} $stackTrace');
},
),
);
}