Flutter Learning 4: Flutter Development Basics (6) Debugging Flutter Applications

Table of contents

0 Preface

1 Flutter exception capture

1.1 Dart single-threaded model

1.2 Flutter exception capture

1.2.1 Flutter framework exception capture

1.2.1.1 Flutter’s default exception catching method

1.2.1.2 Catch exceptions yourself and report them

1.2.2 Other exception capture and log collection

1.2.3 Final error reporting code


0 Preface

This article is a study and summary of the preface to the second edition | "Flutter in Practice·Second Edition" (flutterchina.club) .

1 Flutter exception capture

1.1 Dart single-threaded model

  • Both Java and Objective-C are multi-threaded model programming languages. When any thread triggers an exception and the exception is not caught, it will cause the entire process to exit.
  • Dart and JavaScript are both single-threaded models. If an exception occurs in the program and is not caught, the program will not terminate.

The general operating principle of Dart:

Dart runs in a single thread using a message loop mechanism , which contains two task queues:

  • One is "microtask queue"  microtask queue ,
  • The other is called the "event queue"  event queue .
  • The execution priority of the microtask queue is higher than that of the event queue.

Dart thread running process:

  1. After the entry function main() is executed, the message loop mechanism is started.
  2. First, the tasks in the microtask queue will be executed one by one in first-in , first-out order.
  3. After the event task is executed, the program will exit.
  4. However, new microtasks and event tasks can also be inserted during the execution of event tasks.
  5. In this case, the execution process of the entire thread is always looping and will not exit.
  6. In Flutter, the execution process of the main thread is exactly like this and never terminates.

In Dart:

  • All external event tasks are in the event queue, such as IO, timer, click, and drawing events, etc.
  • Microtasks usually come from within Dart, and there are very few microtasks because the microtask queue has a high priority. If there are too many microtasks, the total execution time will be longer, and the delay of event queue tasks will be longer, which is most intuitive for GUI applications. The performance is relatively laggy, so we must ensure that the microtask queue is not too long.
  • A task can Future.microtask(…)be inserted into the microtask queue via a method.
  • In the event loop, when an exception occurs in a task and is not caught, the program will not exit. The direct result is that the subsequent code of the current task will not be executed. That is to say, an exception in a task is It will not affect the execution of other tasks.

1.2 Flutter exception capture

try/catch/finallyCode block exceptions can be caught in Dart , which is similar to other programming languages.

1.2.1 Flutter framework exception capture

1.2.1.1 Flutter’s default exception catching method

When an exception occurs, Flutter's default handling method is to pop up an ErrorWidget

@override
void performRebuild() {
 ...
  try {
    //执行build方法  
    built = build();
  } catch (e, stack) {
    // 有异常时则弹出错误提示  
    built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
  } 
  ...
}      
1.2.1.2 Catch exceptions yourself and report them
//1.查看_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;
}

//2.核心的是FlutterError.reportError(details);这一句。进入后发现:
static void reportError(FlutterErrorDetails details) {
  ...
  if (onError != null)
    onError(details); //调用了onError回调
}

/*
3. 继续跟踪这里的onError就会发现它是FlutterError的一个静态属性,
   有个名为dumpErrorToConsole的默认处理方法。
*/
static FlutterExceptionHandler onError = dumpErrorToConsole;

If we want to report the exception ourselves, we only need to provide a custom error handling callback, such as:

void reportErrorAndLog(FlutterErrorDetails details){
    ... //上报错误和日志逻辑
}

void main() {
  FlutterError.onError = (FlutterErrorDetails details) {
    reportErrorAndLog(details);
  };
 ...
}

1.2.2 Other exception capture and log collection

In Dart, exceptions are divided into two categories: synchronous exceptions and asynchronous exceptions.

  • Synchronous exceptions can be try/catchcaught by,
  • Asynchronous exceptions can use runZoned(...) methods

For example, the following code cannot catch Futureexceptions: 

try{
    Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
}catch (e){
    print(e)
}

There is a method in Dart runZoned(...) that takes two parameters:

  • zoneValues: Private data of Zone, which can zone[key]be obtained through the instance.
  • zoneSpecification: Some configurations of Zone can customize some code behaviors, such as intercepting print to collect logs, intercepting unhandled asynchronous errors and reporting them.
//runZoned(...)方法定义:
R runZoned<R>(R body(), {
    Map zoneValues, 
    ZoneSpecification zoneSpecification,
}) 
//runZoned(...)方法使用示例
/*
   拦截print,收集日志
   拦截未处理的异步错误,并进行上报
*/
runZoned(
  () => runApp(MyApp()),
  zoneSpecification: ZoneSpecification(
    // 拦截print
    print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
      collectLog(line);
      parent.print(zone, "Interceptor: $line");
    },
    // 拦截未处理的异步错误
    handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone,
                          Object error, StackTrace stackTrace) {
       reportErrorAndLog(details);     
       parent.print(zone, '${error.toString()} $stackTrace');
    },
  ),
);


void collectLog(String line){
    ... //收集日志
}

void reportErrorAndLog(FlutterErrorDetails details){
    ... //上报错误和日志逻辑
}

1.2.3 Final error reporting code

void collectLog(String line){
    ... //收集日志
}

void reportErrorAndLog(FlutterErrorDetails details){
    ... //上报错误和日志逻辑
}

FlutterErrorDetails makeDetails(Object obj, StackTrace stack){
    ...// 构建错误信息
}

void main() {
  var onError = FlutterError.onError; //先将 onerror 保存起来
  FlutterError.onError = (FlutterErrorDetails details) {
    onError?.call(details); //调用默认的onError
    reportErrorAndLog(details); //上报
  };
  runZoned(
  () => runApp(MyApp()),
  zoneSpecification: ZoneSpecification(
    // 拦截print
    print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
      collectLog(line);
      parent.print(zone, "Interceptor: $line");
    },
    // 拦截未处理的异步错误
    handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone,
                          Object error, StackTrace stackTrace) {
      reportErrorAndLog(details);
      parent.print(zone, '${error.toString()} $stackTrace');
    },
  ),
 );
}

Guess you like

Origin blog.csdn.net/D_lunar/article/details/131469441