Technical practice phase II Flutter exception capture

Keywords: Flutter Optimize app

Author: Youmeng + technical expert Yanke

1, Background

Application performance stability is a key link in 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. Our team launched U-APM mobile application performance monitoring products After that, it helps developers locate and solve many online problems. With the increase of users and attention, in the messages of visiting customers and developers, many developers hope that the product can support the exception capture of the fluent framework. I have never developed flutter myself, so I mainly report exceptions by making plug-ins based on the existing product capabilities. This article records the process and problems I have encountered in learning flutter error handling.

2, Flutter exception

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

3, Abnormal characteristics of Flutter

Dart is a single process mechanism, so problems in this process will only affect the current process. Dart uses the event loop mechanism to run the task. When an exception occurs to a task and is not captured, the program will not exit, and the direct result is that the subsequent code of the current task will not be executed, In other words, exceptions in one task will not affect the execution of other tasks, and the running state of each task is independent of each other.
For example, we can capture it through a try catch mechanism similar to Java. But unlike Java, Dart programs do not force us to handle exceptions.

4, Flutter anomaly classification

In the development of fluent, exceptions can be divided into framework exceptions and App exceptions according to different sources. Flutter provides different capture methods for these two exceptions. Framework exceptions are caused by the flutter framework. They are usually caused by the underlying exception judgment of the flutter framework caused by wrong application code. The App exception is the exception of the application code, which is usually caused by the exception thrown by other modules of the application layer. According to the execution timing of exception code, App exceptions can be divided into two categories, namely synchronous exceptions and asynchronous exceptions.

5, Capture mode

1. Capture method of APP exceptions

To catch synchronization exceptions, use the try catch mechanism:

// Catch synchronization exceptions using try catch
try {
  throw StateError('This is a Dart exception.');
}
catch(e) {
  print(e);
}

Catch asynchronous exceptions. Use the catchrror statement provided by Future:

// Catch asynchronous exceptions using catchrror
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, can't there be a way to monitor both synchronous and asynchronous exceptions?
The answer is yes.
Fluent provides the Zone. Runzone method to manage all exceptions in the code. We can assign a Zone to the code execution object. In Dart, Zone represents the environment range of code execution. Its concept is similar to sandbox, and different sandboxes are isolated from each other. If we want to observe the exceptions in code execution in the sandbox, the sandbox provides an onError callback function to intercept those uncapped exceptions in the code execution object. No more nonsense,
Show me the code!

runZoned(() {
  // Synchronization exception
  throw StateError('This is a Dart exception.');
}, onError: (dynamic e, StackTrace stack) {
  print('Sync error caught by zone');
});
runZoned(() {
  // Asynchronous exception
  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 focus on capturing unhandled exceptions in the fluent application, I finally put the runApp statement in the main function in the Zone. In this way, when an exception is detected in the code, it can be handled uniformly according to the obtained exception context information:

runZoned<Future<Null>>(() async {
  runApp(MyApp());
}, onError: (error, stackTrace) async {
 //Do sth for error
});

2.Framework exception capture method

The fluent framework captures exceptions in many key methods. If we want to report exceptions 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 can handle the above exceptions uniformly?

3. Summary (a set of code captures 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);
  });

A sentence appears in the code, that is, WidgetsFlutterBinding.ensureInitialized(). When I comment out this line of code, the framework exception cannot be caught.
After a long time of trouble, we finally found out the reason:

The figure above shows the architecture layer of the Flutter. WidgetFlutterBinding is used to interact with the Flutter engine. Our APM product needs to call the native code to initialize, and because the plug-in needs to use the platform channel to call the native code, which is done asynchronously, you must call ensureInitialized() to ensure that you have an instance of WidgetsBinding
From 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 is invoked in runApp,
WidgetsFlutterBinding.ensureInitialized() method to initialize operations, you must call WidgetsFlutterBinding.ensureInitialized() in runZonedGuarded.

6, Abnormal reporting

The overall scheme of exception reporting is to add interfaces through existing plug-ins to bridge the user-defined exception reporting interfaces of Android APM and iOS APM libraries.
Plug in add function

static void postException(error, stack) {
    List<dynamic> args = [error,stack];
    //Escalate exceptions and stacks to umapm
    _channel.invokeMethod("postException",args);
  }

Android client calls custom exception reporting:

private void postException(List args){
    String error = (String)args.get(0);
    String stack = (String)args.get(1);
    UMCrash.generateCustomLog(stack,error);
  }

iOS calls custom exception reporting:

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 dry goods in this issue. I hope our technical content can better help developers solve problems. We will accompany developers to make progress and grow together.

Sweep and join the alliance + technology community
Discuss the latest developments of mobile development with more than 1000 + mobile developers

Welcome to click[ Friendship Alliance+ ], learn about Youmeng + the latest mobile technology

Welcome to the public official account.

Posted by chrima on Mon, 06 Dec 2021 15:39:34 -0800