【发布时间】:2022-01-19 00:35:40
【问题描述】:
我有一个简单的 Flutter 插件,它通过 MethodChannel 与一些原生 Android 代码进行通信。以下是插件提供的方法的简单示例:
Future<void> doSomethingImportant(int address, SomeImportantData importantData) async {
String jsonButStillImportant = jsonEncode(importantData.toJson());
return methodChannel.invokeMethod('doSomethingImportant', {"address": address, "data": jsonButStillImportant});
}
doSomethingImportant()(java) 方法完成后,通过调用将结果传递给 Flutter:
public void onSuccess(Result result, Object value) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
result.success(value);
Log.d("Debug", "Why does this occasionally cause a timeout at Flutter?!?");
}
});
}
然后,在测试期间,该方法被这样调用:
void _someTestMethod() async {
await doSomethingImportant(anAddress, theMostImportantDataInTheWorld).timeout(const Duration(seconds: 1)).catchError((e) {
print("Something's wrong here: "+e.toString());
return;
});
}
这在大多数情况下都运行良好。然而,有时测试会失败,因为doSomethingImportant() 遇到超时,并且 logcat 会显示以下内容:
2021-12-15 19:56:50.919 D/Debug: Why does this occasionally cause a timeout at Flutter?!?"
2021-12-15 19:56:51.697 I/flutter: Something's wrong here: TimeoutException after 0:00:01.000000: Future not completed
现在我想知道什么可能导致超时以及如何防止它。值得注意的是,超时消息总是在调用result.success() 后大约700-800 毫秒出现。
编辑 1:
问题是有时未来没有完成,即使result.success() 被调用。这只是偶尔发生,但如果发生,超时目的是优雅地退出调用函数。不幸的是,简单地删除超时不是一种选择,因为它会永远阻塞。
编辑 2 - 更多背景信息:
请注意,doSomethingImportant() 和_someTestMethod() 的上述代码 sn-ps 只是用于说明方法外观的示例。 (除了onSuccess(),这个是生产代码的精确副本)。
实际的插件控制蓝牙 Mesh 网络。 _someTestMethod() 发送和接收数据以验证网状网络和运行在各个节点上的固件是否正在执行它们应该执行的操作。 _someTestMethod() 中的逻辑如下所示:
- 将数据发送到网格中。
- 从网格中读取数据。
- 验证读取的数据是否符合预期结果。
之所以选择await/timeout() 方法,是因为每一步都依赖于上一步的成功完成。将这些调用嵌套在 .then() 回调中是一种替代方法,但我更喜欢从上到下的 await/timeout()-spaghetti-code 而不是嵌套的 .then() 回调。
问题是,Android 时不时会调用MethodChannel.Result.success(),但Flutter 并没有完成Future。如果发生这种情况,Future 将永远保持未完成状态,logcat 也不会提供任何信息。
同样,这种情况只是偶尔发生。大多数时候它工作得很好。
现在的问题是,这里出了什么问题?还有其他方法可以说服Future 完成吗?欢迎任何想法谢谢!
【问题讨论】:
标签: flutter flutter-method-channel