Flutter - exception capture

1. Background

The stability of application performance is a very critical part of a good user experience. In order to better ensure the stability of application performance, exception capture plays a vital role in ensuring the stability of online products. After our team launched the U-APM mobile application performance monitoring product, it helped developers locate and solve many online problems. With the increase in the number of users and the increase in attention, in the messages of visiting customers and developers, many developers hope that the product can support the exception capture of the flutter framework. I have never done flutter development myself, so I mainly report exceptions through plug-ins based on existing product capabilities. This article records the process of learning flutter error handling and the problems I encountered.

2. Flutter exception

A Flutter exception refers to an error event that occurs unexpectedly when the Dart code in a Flutter program is running.

3. Abnormal characteristics of Flutter

Dart is a single-process mechanism, so when a problem occurs in this process, it will only affect the current process. Dart uses the event loop mechanism to run tasks. When an exception occurs in a task and is not caught, the program will not exit, but directly The result is that the subsequent code of the current task will not be executed, which means that an exception in one task will not affect the execution of other tasks, and the running status of each task is independent of each other.

For example: we can catch it through a try-catch mechanism similar to Java. But unlike Java, Dart programs don't force us to handle exceptions.

4. Flutter exception classification

In the development of Flutter, exceptions can be divided into Framework exceptions and App exceptions according to the source of exceptions. Flutter provides different capture methods for these two types of exceptions. Framework exceptions are exceptions raised by the Flutter framework, and are usually caused by incorrect application codes that cause abnormal judgments at the bottom of the Flutter framework. For the App exception, it is the exception of the application code, which is usually caused by the exception thrown by other modules of the application layer that has not been processed. According to the execution timing of exception codes, app exceptions can be divided into two categories, namely synchronous exceptions and asynchronous exceptions.

5. Capture method

1. App exception capture method

Catching synchronous exceptions uses the try-catch mechanism:

// 使用 try-catch 捕获同步异常

try {

 throw StateError('This is a Dart exception.');

}

catch(e) {

 print(e);

}

To catch asynchronous exceptions use the catchError statement provided by Future:

// 使用 catchError 捕获异步异常

Future.delayed(Duration(seconds: 1))

   .then((e) => throw StateError('This is a Dart exception in Future.'))

   .catchError((e)=>print(e));

Seeing this, it is estimated that many people will ask in their hearts, can't there be a way to monitor both synchronous and asynchronous exceptions?

The answer is yes.

Flutter provides the Zone.runZoned method to manage all exceptions in your code. We can specify a Zone for the code execution object. In Dart, a Zone represents an environment range for code execution. Its concept is similar to a sandbox, and different sandboxes are isolated from each other. If we want to observe exceptions in the code execution in the sandbox, the sandbox provides the onError callback function to intercept those uncaught exceptions in the code execution object. No more nonsense,

Show me the code

runZoned(() {

 // 同步异常

 throw StateError('This is a Dart exception.');

}, onError: (dynamic e, StackTrace stack) {

 print('Sync error caught by zone');

});

runZoned(() {

 // 异步异常

 Future.delayed(Duration(seconds: 1))

     .then((e) => throw StateError('This is a Dart exception in Future.'));

}, onError: (dynamic e, StackTrace stack) {

 print('Async error aught by zone');

});

In order to be able to centrally capture unhandled exceptions in the Flutter application, I finally placed the runApp statement in the main function in the Zone. In this way, when an exception is detected in the code, it can be processed uniformly according to the obtained exception context information:

runZoned>(() async {

 runApp(MyApp());

}, onError: (error, stackTrace) async {

//Do sth for error

});

2. Framework exception capture method

The Flutter framework provides us with exception capture in many key methods. If we want to report the exception ourselves, we only need to provide a custom error handling callback, such as:

void main() {

 FlutterError.onError = (FlutterErrorDetails details) {

   reportError(details);

 };

...

}

Is there a set of code that fell from the sky that can handle the above exceptions uniformly?

3. Summary (a set of codes to catch all exceptions)

runZonedGuarded(() async {

   WidgetsFlutterBinding.ensureInitialized();

FlutterError.onError = (FlutterErrorDetails details) {

     myErrorsHandler.onError(details.exception,details.stack);

   };

   runApp(MyApp());

 }, (Object error, StackTrace stack) {

   myErrorsHandler.onError(error, stack);

 });

There is a sentence in the code, the code that has never appeared in the appeal is WidgetsFlutterBinding.ensureInitialized(), when I comment out this line of code, the framework exception cannot be caught.

I was troubled for a long time and finally found the reason:

The above figure is the architectural layer of Flutter, and WidgetFlutterBinding is used to interact with the Flutter engine. Our APM product needs to call native code to initialize, and since the plugin needs to use the platform channel to call native code, which is done asynchronously, you must call ensureInitialized() to ensure you have an instance of WidgetsBinding .

From  the docs  :

Returns an instance of the WidgetsBinding, creating and initializing it if necessary. If one is created, it will be a WidgetsFlutterBinding. If one was previously initialized, then it will at least implement WidgetsBinding.

Note: If your application calls the WidgetsFlutterBinding.ensureInitialized() method in runApp to perform some initialization operations, you must call WidgetsFlutterBinding.ensureInitialized() in runZonedGuarded

6. Abnormal reporting

The overall solution for exception reporting is to add interfaces through existing plug-ins, and bridge the custom exception reporting interfaces of the Android APM and iOS APM libraries.

Plugin add function

static void postException(error, stack) {

   List args = [error,stack];

   //将异常和堆栈上报至umapm

   _channel.invokeMethod("postException",args);

 }

The Android side calls custom exception reporting:

Call the custom exception report on the iOS side:

if ([@"postException" isEqualToString:call.method]){

       NSString* error = arguments[0];

       NSString* stack = arguments[1];

       [UMCrash reportExceptionWithName:@"Flutter" reason:error stackTrace:stack terminateProgram:NO];

}

The above is the introduction of the content of this issue. I hope that our technical content can better help developers solve problems. We will accompany developers to make progress and grow together.

Original Technical Practice Phase 2|Flutter Exception Capture-Alibaba Cloud Developer Community

Guess you like

Origin blog.csdn.net/qq_27909209/article/details/131432837