<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # Dart線程模型及異常捕獲 ## Dart單線程模型 在Java和OC中,如果程序發生異常且沒有被捕獲,那么程序將會終止,但在Dart或JavaScript中則不會,究其原因,這和它們的運行機制有關系,Java和OC都是多線程模型的編程語言,任意一個線程觸發異常且沒被捕獲時,整個進程就退出了。但Dart和JavaScript不同,它們都是單線程模型,運行機制很相似(但有區別),下面我們通過Dart官方提供的一張圖來看看dart大致運行原理: ![](https://box.kancloud.cn/eb7484c9046617a9dec2e7f0507bfcde_471x506.png) Dart 在單線程中是以消息循環機制來運行的,其中包含兩個任務隊列,一個是“微任務隊列” **microtask queue**,另一個叫做“事件隊列” **event queue**。從圖中可以發現,微任務隊列的執行優先級高于事件隊列。 現在我們來介紹一下Dart線程運行過程,如上圖中所示,入口函數 main() 執行完后,消息循環機制便啟動了。首先會按照先進先出的順序逐個執行微任務隊列中的任務,當所有微任務隊列執行完后便開始執行事件隊列中的任務,事件任務執行完畢后再去執行微任務,如此循環往復,生生不息。 在Dart中,所有的外部事件任務都在事件隊列中,如IO、計時器、點擊、以及繪制事件等,而微任務通常來源于Dart內部,并且微任務非常少,之所以如此,是因為微任務隊列優先級高,如果微任務太多,執行時間總和就越久,事件隊列任務的延遲也就越久,對于GUI應用來說最直觀的表現就是比較卡,所以必須得保證微任務隊列不會太長。值得注意的是,我們可以通過`Future.microtask(…)`方法向微任務隊列插入一個任務。 在事件循環中,當某個任務發生異常并沒有被捕獲時,程序并不會退出,而直接導致的結果是**當前任務**的后續代碼就不會被執行了,也就是說一個任務中的異常是不會影響其它任務執行的。 ## Flutter異常捕獲 Dart中可以通過`try/catch/finally`來捕獲代碼塊異常,這個和其它編程語言類似,,如果讀者不清楚,可以查看Dart語言文檔,不在贅述,下面我們看看Flutter中的異常捕獲。 ### Flutter框架異常捕獲 Flutter 框架為我們在很多關鍵的方法進行了異常捕獲。這里舉一個例子,當我們布局發生越界或不合規范時,Flutter就會自動彈出一個錯誤界面,這是因為Flutter已經在執行build方法時添加了異常捕獲,最終的源碼如下: ``` @override void performRebuild() { ... try { //執行build方法 built = build(); } catch (e, stack) { // 有異常時則彈出錯誤提示 built = ErrorWidget.builder(_debugReportException('building $this', e, stack)); } ... } ``` 可以看到,在發生異常時,Flutter默認的處理方式是彈一個ErrorWidget,但如果我們想自己捕獲異常并上報到報警平臺的話應該怎么做?我們進入`_debugReportException()`方法看看: ``` FlutterErrorDetails _debugReportException( String context, dynamic exception, StackTrace stack, { InformationCollector informationCollector }) { //構建錯誤詳情對象 final FlutterErrorDetails details = FlutterErrorDetails( exception: exception, stack: stack, library: 'widgets library', context: context, informationCollector: informationCollector, ); //報告錯誤 FlutterError.reportError(details); return details; } ``` 我們發現,錯誤是通過`FlutterError.reportError`方法上報的,繼續跟蹤: ``` static void reportError(FlutterErrorDetails details) { ... if (onError != null) onError(details); //調用了onError回調 } ``` 我們發現`onError`是`FlutterError`的一個靜態屬性,它有一個默認的處理方法 `dumpErrorToConsole`,到這里就清晰了,如果我們想自己上報異常,只需要提供一個自定義的錯誤處理回調即可,如: ``` void main() { FlutterError.onError = (FlutterErrorDetails details) { reportError(details); }; ... } ``` 這樣我們就可以處理那些Flutter為我們捕獲的異常了,接下來我們看看如何捕獲其它異常。 ### 其它異常捕獲與日志收集 在Flutter中,還有一些Flutter沒有為我們捕獲的異常,如調用空對象方法異常、Future中的異常。在Dart中,異常分兩類:同步異常和異步異常,同步異常可以通過`try/catch`捕獲,而異步異常則比較麻煩,如下面的代碼是捕獲不了`Future`的異常的: ``` try{ Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx")); }catch (e){ print(e) } ``` Dart中有一個`runZoned(...)` 方法,可以給執行對象指定一個Zone。Zone表示一個代碼執行的環境范圍,為了方便理解,讀者可以將Zone類比為一個代碼執行沙箱,不同沙箱的之間是隔離的,沙箱可以捕獲、攔截或修改一些代碼行為,如Zone中可以捕獲日志輸出、Timer創建、微任務調度的行為,同時Zone也可以捕獲所有未處理的異常。下面我們看看`runZoned(...)`方法定義: ``` R runZoned<R>(R body(), { Map zoneValues, ZoneSpecification zoneSpecification, Function onError, }) ``` - zoneValues: Zone 的私有數據,可以通過實例`zone[key]`獲取,可以理解為每個“沙箱”的私有數據。 - zoneSpecification:Zone的一些配置,可以自定義一些代碼行為,比如攔截日志輸出行為等,舉個例子: 下面是攔截應用中所有調用`print`輸出日志的行為。 ``` main() { runZoned(() => runApp(MyApp()), zoneSpecification: new ZoneSpecification( print: (Zone self, ZoneDelegate parent, Zone zone, String line) { parent.print(zone, "Intercepted: $line"); }), ); } ``` 這樣一來,我們APP中所有調用`print`方法輸出日志的行為都會被攔截,通過這種方式,我們也可以在應用中記錄日志,等到應用觸發未捕獲的異常時,將異常信息和日志統一上報。ZoneSpecification還可以自定義一些其他行為,讀者可以查看API文檔。 - onError:Zone中未捕獲異常處理回調,如果開發者提供了onError回調或者通過`ZoneSpecification.handleUncaughtError`指定了錯誤處理回調,那么這個zone將會變成一個error-zone,該error-zone中發生未捕獲異常(無論同步還是異步)時都會調用開發者提供的回調,如: ``` runZoned(() { runApp(MyApp()); }, onError: (Object obj, StackTrace stack) { var details=makeDetails(obj,stack); reportError(details); }); ``` 這樣一來,結合上面的`FlutterError.onError`我們就可以捕獲我們Flutter應用中全部錯誤了!需要注意的是,error-zone內部發生的錯誤是不會跨越當前error-zone的邊界的,如果想跨越error-zone邊界去捕獲異常,可以通過共同的“源”zone來捕獲,如: ``` var future = new Future.value(499); runZoned(() { var future2 = future.then((_) { throw "error in first error-zone"; }); runZoned(() { var future3 = future2.catchError((e) { print("Never reached!"); }); }, onError: (e) { print("unused error handler"); }); }, onError: (e) { print("catches error of first error-zone."); }); ``` ### 總結 我們最終的異常捕獲和上報代碼如下: ``` void collectLog(String line){ ... //收集日志 } void reportErrorAndLog(FlutterErrorDetails details){ ... //上報錯誤和日志邏輯 } FlutterErrorDetails makeDetails(Object obj, StackTrace stack){ ...// 構建錯誤信息 } void main() { FlutterError.onError = (FlutterErrorDetails details) { reportErrorAndLog(details); }; runZoned( () => runApp(MyApp()), zoneSpecification: ZoneSpecification( print: (Zone self, ZoneDelegate parent, Zone zone, String line) { collectLog(line); //手機日志 }, ), onError: (Object obj, StackTrace stack) { var details = makeDetails(obj, stack); reportErrorAndLog(details); }, ); } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看