最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Dart语言async函数不正确的异常处理引发的异常

    正文概述 掘金(eggfly)   2021-06-17   816

    小测试

    这样写platform channel调用并处理异常的代码,有啥问题?

    const MethodChannel channel = MethodChannel('my_platform_channel');
    try {
      channel.invokeMethod('my_method');
    } catch (e) {
      debugPrint('Caught an exception: $e'); 
    }
    

    结果往下看:

    代码这样直接用try-catch是无效的,不会走到catch块里!且日志里直接显示Unhandled Exception。

    因为channel.invokeMethod是一个异步函数,它的返回值是Future:

    // 省略巨长的官方函数文档
    
    Future<T?> invokeMethod<T>(String method, [ dynamic arguments ]) {
      return _invokeMethod<T>(method, missingOk: false, arguments: arguments);
    
    }
    

    更多的分析和正确的写法,请看下文。

    最佳实践

    1. 如果使用了await调用了异步函数(返回Future的函数)并且需要做异常处理,在await外面直接try-catch即可
    2. 如果没有使用await调用了异步函数(返回Future的函数)并且需要做异常处理,千万不要直接try-catch(否则起不到捕获作用,代码会继续误导下一个人),需要使用onError或者catchError做对应的处理或者re-throw。

    背景

    先说下这个问题的背景是,调查了一个webview_flutter和一个settings插件的platform调用异常问题,两个异常很类似:

    有如下异常:

    MissingPluginException(No implementation found for method evaluateJavascript on channel plugins.flutter.io/webview_0)
    #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:253)
    <asynchronous suspension>
    // 然后就没有然后了,没有调用者的行号
    

    这应该是一个类似于上面提到的 channel.invokeMethod('evaluateJavascript') 的调用。在查看webview plugin部分出现的invokeMethod的dart代码后,看到代码是这样写的:

    // js_bridge.dart
                """ // 省略一些js代码
                }
                return result;
            })($s)""";
      try {
        _webViewController.evaluateJavascript(js);
      } catch(e) {
      }
    }
    

    evaluateJavascript里面的实现是这样

    @override
    Future<String> evaluateJavascript(String javascriptString) {
      return _channel.invokeMethod<String>(
          'evaluateJavascript', javascriptString);
    }
    

    第一反应,? try-catch 为什么没有catch住异常呢?

    做了5个测试

    看下面的5种情况的异常处理的测试代码,前提是:getPlatformVersion故意弄成了一个没有注册过的channel method的名字,一定会抛出MissingPluginException异常。

    那么猜想5种写法分别会得到什么结果?

    class FlutterPluginDemo {
      static const MethodChannel _channel =
          const MethodChannel('flutter_plugin_demo');
    
      static Future<String> get platformVersion async {
        var future;
        // 第1种错误处理: 错误
        try {
          future = _channel.invokeMethod('getPlatformVersion');
          print('1. future.type: ${future.runtimeType}'); // invoke的返回值是个Future
        } catch (e) {
          // 所以这里基本永远不会走到
          print('1. caught a exception: $e');
        }
    
        // 第2种错误处理: 正确
        try {
          var result = await future;
          print('2. result.type: ${result.runtimeType}');
        } catch (e) {
          print('2. caught a exception by try-catch: $e'); // 这里可以正确catch住上面第1个future的异常
        }
    
        // 第3种错误处理: 正确
        _channel.invokeMethod('getPlatformVersion').onError(
            (error, stackTrace) => print('3. caught a exception by onError: $error'));
    
        // 第4种:不处理,也不用await,会抛出异常,但是<asynchronous suspension>找不到调用来源代码的行号信息
        _channel.invokeMethod('getPlatformVersion');
    
        // 第5种: 默认不处理异常,但是用await,并且<asynchronous suspension>可以找到来源行号信息
        await _channel.invokeMethod('getPlatformVersion');
        return "test";
      }
    }
    

    结果是(看图):

    Dart语言async函数不正确的异常处理引发的异常

    也就是:

    • 第1种,是错误的,catch块永远不会调用到,且future变量的type是Future<dynamic>
    • 第2种,是正确的,拿到future对象并对它做了await操作,此时catch可以正常捕获
    • 第3种,是正确的,onError是Future的一个方法,可以在内部抛出异常的时候,转成异常处理的lambda函数,并且此时这个异常从unhandled转换成handled状态
    • 第4种,是不推荐的,不用await且不做异常处理,直接在日志中抛出如下异常:
    com.eggfly.flutter_plugin_demo_example E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: MissingPluginException(No implementation found for method getPlatformVersion on channel flutter_plugin_demo)
        #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:7)
        <asynchronous suspension>
    

    并且注意到,这种异常的里面,是没有调用来源信息的(没有调用地方的行号,只有platform_channel.dart的行号)

    Dart语言async函数不正确的异常处理引发的异常

    • 第5种,是不推荐的,使用了await但是没有onError或者try-catch做异常处理,遇到异常可能日志会出现如下异常信息:
    com.eggfly.flutter_plugin_demo_example E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: MissingPluginException(No implementation found for method getPlatformVersion on channel flutter_plugin_demo)
    
        #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:7)
    
        <asynchronous suspension>
    
        #1      FlutterPluginDemo.platformVersion (package:flutter_plugin_demo/flutter_plugin_demo.dart:35:5)
    
        <asynchronous suspension>
    
        #2      _MyAppState.initPlatformState (package:flutter_plugin_demo_example/main.dart:30:25)
    
        <asynchronous suspension>
    

    Dart语言async函数不正确的异常处理引发的异常

    注意到这种使用了await的情况下,里面是有嵌套的异步调用信息的。这里对于解决Dart异常会有很大帮助。

    解决方法

    • webview_flutter的Dart异常可以改成:
    // try catch cannot catch exceptions using Future
    _webViewController.evaluateJavascript(js).catchError((e) {});
    

    注:在evaluateJavascript的这里的场景下catchError暂时不做其他处理;

    在其他场景下,可以根据具体exception的类型做区分,并相应处理或者re-throw出来。

    参考 & read more:

    • 官方文档: Future 和错误处理

    起源地下载网 » Dart语言async函数不正确的异常处理引发的异常

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元