为什么MethodChannel.Result.success()有时会导致Flutter超时



我有一个简单的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()的代码片段只是示例,用于说明这些方法的外观。(除了onSuccess(),这是生产代码的精确副本)。

实际的插件控制蓝牙网状网络。_someTestMethod()发送和接收数据,以验证网状网络和运行在各个节点上的固件是否在做它们应该做的事情。_someTestMethod()中的逻辑如下:

  1. 将数据发送到网格中
  2. 从网格中读取数据
  3. 验证读取的数据是否与预期结果匹配

之所以选择await/timeout()方法,是因为每一步都取决于前一步的成功完成情况。将这些调用嵌套在.then()回调中是一种选择,但与嵌套的.then()回调相比,我更喜欢自上而下的await/timeout()-意大利面条代码。

问题是,偶尔会在Android上调用MethodChannel.Result.success(),但Flutter并没有完成Future。如果发生这种情况,未来将永远处于未完成状态,logcat也不会告诉任何事情。

同样,这种情况只是偶尔发生。大多数时候,它运行得很好。

现在的问题是,这里出了什么问题?有没有其他方法可以说服Future完成?欢迎有任何想法,谢谢!

现在我想知道是什么可能导致超时以及如何防止超时。值得注意的是,超时消息总是在调用result.success()后700-800毫秒左右出现。

你知道如何修复(甚至调试)这个超时的来源吗?非常感谢。

您正在使用.timeout(const Duration(seconds: 1)),因此超时。如果您不希望它超时,删除该代码。

你能澄清一下你面临的问题是什么吗?你是否对你的工作没有在1秒内完成感到失望,或者你只是没有意识到你添加了超时?


编辑后:

使用await+timeout并不能正常地退出函数,它实际上抛出了一个错误。致";优雅地";处理Future可能无法立即完成的情况,而不是使用await,而是使用.then。这样,平台方法调用下面的任何代码都不会延迟。当然,在您的平台方法中不会有结果。如果您需要立即得到结果,那么await+timeout+catchError.then都不会帮助您。

任意选择1秒(或任何其他时间)运行代码然后抛出错误肯定会带来问题,尤其是在用户使用各种设备的生产应用程序中。在速度较慢的设备上,这种情况可能会合法发生,您应该优雅地处理它。这也可能影响调试,比如说,你调用了一个平台方法,然后在断点处暂停,代码会抛出错误,因为计时器仍然在运行。多么糟糕的调试经历啊。我强烈建议在这种情况下不要使用timeout

要帮助调试,请执行以下操作:

  • 在Android Studio中打开android项目窗口,然后:
    • 从该窗口运行调试器(启动Android应用程序),并附加调试器
    • 从Flutter窗口运行调试器,并将android项目窗口调试器附加到正在运行的应用程序。应用程序
  • result.success(value);行上放置一个断点,并观察是否正在调用它。
    • 也许new Handler(Looper.getMainLooper()).post(new Runnable() {从未被调用,在这种情况下,您必须理解为什么该可运行文件从未被发布。我不确定您是否真的运行了上面发布的示例,或者您是否只是想调试一个更复杂的应用程序
    • 也许另一个问题正在发生,可能会显示一个更有用的错误。(你没有分享太多日志,看看logcat或pidcat。)。一个疯狂的猜测是主线程现在很忙,正在做一些你不应该在主线程上做的工作,或者只是在做Flutter工作(可能性较小)

最新更新