0

我有一个简单的 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(),这是生产代码的精确副本)。

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

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

选择该await/timeout()方法是因为每个步骤都取决于上一步的成功完成。将这些调用嵌套在.then()回调中是一种替代方法,但我只是更喜欢从上到下的await/timeout()意大利面条代码而不是嵌套.then()回调。

问题是,MethodChannel.Result.success()在 Android 上时不时地调用它,但 Flutter 并没有完成Future. 如果发生这种情况,Future 将永远保持未完成状态,并且 logcat 也不会提供任何信息。

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

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

4

1 回答 1

0

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

您对如何修复(甚至调试)此超时的来源有任何想法吗?谢谢!

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

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


编辑后:

使用await+timeout不会优雅地退出函数,它实际上会引发错误。为了“优雅地”处理这种情况,Future 可能不会立即完成,而不是使用await.then而是使用。这样,平台方法调用下方的任何代码都不会延迟。当然,您的平台方法中不会有结果。如果您立即需要该结果,那么await++timeoutcatchError不会.then帮助您。

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

帮助调试:

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