小测试
这样写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);
}
更多的分析和正确的写法,请看下文。
最佳实践
- 如果使用了await调用了异步函数(返回Future的函数)并且需要做异常处理,在await外面直接try-catch即可
- 如果没有使用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";
}
}
结果是(看图):
也就是:
- 第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的行号)
- 第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>
注意到这种使用了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 和错误处理
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!