【问题标题】:Unable to understand Flutter Isolate workaround for "'Window_sendPlatformMessage' (4 arguments) cannot be found" error无法理解“找不到‘Window_sendPlatformMessage’(4 个参数)”错误的 Flutter Isolate 解决方法
【发布时间】:2019-12-19 08:57:43
【问题描述】:

我正在将一个非常简单的标签打印应用程序从 Swift 转换为 Flutter。这个应用程序的一个主要组件是本地缓存的数据库,用于离线访问产品。

虽然我可以在主线程上下载和缓存所有 12,000 多种产品,但对于如此繁重的操作来说,这是一个糟糕的、糟糕的解决方案,我真的很想避免这种情况,除非万不得已。即使作为最后的手段,如果后台线程无法用于基本任务(例如将在线数据缓存到本地数据库),我也很难说服我的雇主让我从 Swift 迁移到 Flutter。

因此,我一直在探索 Isolates。在此过程中,每当我在Isolate 上尝试使用sqflitepath_provider 缓存数据时,都会遇到错误"'Window_sendPlatformMessage' (4 arguments) cannot be found"
现在,我正在阅读 githubstackoverflow 上的内容,即 Isolates 不支持使用插件(可能是包?),除非以一种不稳定的解决方法。我尝试了plugin,当我尝试使用它时,它只会让我的应用程序崩溃,并带有非常神秘的堆栈跟踪,所以看起来解决方法是唯一的方法。

我大约一周前刚刚开始使用 Flutter 构建,大约两天前才开始使用 Isolates,所以我对所有内容的基本了解目前都很浅。在颤振 repo 上,有一个 comment 似乎概述了插件问题的解决方法,我希望这将允许我使用 path_providersqflite 来缓存我从我的 Web API 获取的数据。

能否请一位更有经验的 Flutter 开发人员将这个解释分解成小题大做?


编辑:

正如下面的答案所述,sqflite 已经是异步的,并且显然在与主线程不同的线程上运行,所以看起来我的具体情况将通过使用计算来解决我的 dart-pure API 调用获取 JSON 数据并使用常规 sqflite 进行存储。尽管如此,仍然需要一个初学者指南来了解不稳定的 Isolate 变通方法,所以我将问题留待解决。

【问题讨论】:

    标签: plugins flutter dart dart-isolates


    【解决方案1】:

    尝试解释

    我也发现该线程中的解决方法帖子的措辞非常混乱,我也无法完全理解它,但是发帖人似乎建议您使用setMockMessageHandler 来拦截数据来自invokeMethod

    setMockMessageHandler 覆盖处理函数,否则它将获取编码数据并将其传递到本机端,从而允许您使用自己的函数。因此,当您为通道设置模拟消息处理程序时,您可以“劫持”数据并通过 SendPort 将其传递回您的主隔离区。

    在主隔离上获得消息后,您可以使用BinaryMessenger.send 将数据发送到其预期目的地。如果您在隔离区而不是主隔离区,这就是中断的步骤,所以现在您已经绕过了这个问题。

    一旦您将数据接收回主线程,您就将其传递回隔离区,并使用您使用BinaryMessages.setMessageHandler 创建的自定义消息处理程序在那里对其进行解码。

    解决方案包

    如果您仍然需要它,我制作了一个包 (Isolate Handler),使用的解决方法与您询问的解决方法非常相似。

    依赖setMockMessageHandler 的任何解决方法的缺点当然是您需要向它提供您将使用的频道名称。您可以通过查找 MethodChannels 的设置位置在源文件中找到这些频道名称。

    似乎 sqflite 使用 com.tekartik.sqflite 而 path_provider 使用 plugins.flutter.io/path_provider,因此为了在 Isolate Handler 的隔离中使用它们,您需要执行以下操作:

    IsolateHandler().spawn(
        entryPoint,
        channels: [
          MethodChannel('com.tekartik.sqflite'),
          MethodChannel('plugins.flutter.io/path_provider'),
        ]
    );
    
    void entryPoint(HandledIsolateContext context) {
      final messenger = HandledIsolate.initialize(context);
      // <Your previous isolate entry point here>
    }
    

    2019 年 9 月 4 日更新:

    我最终通过从本机端开始隔离来为自己解决了这个问题。我从官方 Flutter 插件android_alarm_manager 中获得灵感。虽然该插件是特定于 Android 的,并且对于我的特定项目来说已经足够了,但可以在 iOS 中执行它,以及现在存档的 location_background_plugin 以及下面引用的 Medium 帖子中所示。

    一位 Flutter 开发人员关于从本机端启动隔离的过程的非常全面(如果有点过于关注地理围栏)的帖子是 available on Medium

    帖子中采取的步骤总结

    1. (GitHub) 使用PluginUtilities.getCallbackHandle在 Dart 端设置一个引用回调调度程序的插件
    2. (GitHub) 创建回调调度程序本身,它:
      • 初始化您要访问的 MethodChannel。
      • 调用WidgetsFlutterBinding.ensureInitialized() 设置MethodChannel 所需的内部状态。
      • 在 MethodChannel(s) 上调用 setMethodCallHandler 以侦听来自插件本机端的后台事件,并在事件触发时调用您的回调。
      • 最终使用invokeMethod 提醒本机端插件回调处理程序已准备好处理事件。
    3. 实现本机代码以执行后台执行。
    4. 设置权限以允许后台作为服务执行。
      • 安卓
        • (GitHub) 使用 AndroidManifest.xml 将插件对象注册为服务。
        • (GitHub) 创建自定义FlutterActivity
        • (GitHub) 更新AndroidManifest.xml中的application字段,设置为新创建的FlutterActivity
      • iOS
        • (GitHub)。修改Info.plist请求权限
        • (GitHub) 从应用程序的AppDelegate 设置对应用程序插件注册者的引用。
    5. (GitHub) 应用程序启动时在 Dart 端初始化插件(例如从main。)

    结论

    这绝不是一个简单的解决方案,我仍然希望 Flutter 团队能提供一个更好的解决方案,但它至少是一个有效的解决方案,没有任何问题或意外行为。

    我认为这是目前唯一可以投入生产的解决方案。

    【讨论】:

    • 有趣。但是 sqflite 在事务方面不是交叉隔离安全的,因此如果有并发写入,您可能会收到损坏的数据,所以我不确定它是否是一个安全的解决方案(目前)。
    • 这看起来很有希望!我可能无法在几周内对其进行测试,因此我投了赞成票,但尚未将其标记为答案。一旦我有机会进行测试,我会告诉你这对我来说是如何工作的。
    • @A.L.Srine 我最终为自己的项目找到了更好的解决方案,而是从本机端开始隔离。我会更新我的帖子。
    【解决方案2】:

    我不会准确回答您的问题(如何使用隔离),但我建议此时不要使用隔离。 sqflite 在后台线程中运行。是的,准备数据将在主线程中,因此您绝对不应该一次保存 12000 个项目,而是在事务中下载/保存 50 个项目(或 100 或 1000 个,具体取决于每个项目的大小 - 进行实验)(更好的批次)应该没问题。

    对于大量项目的密集计算(可能包括下载数据的 json 解码或准备地图以保存 sqflite),您可以使用computeflutter 方法。

    如果您需要显示完整列表(如果需要,可以分页),这可能会涉及到一些智能编码,但我认为没有什么可以阻止您“在后台”执行您需要的操作,即不阻塞 UI。

    【讨论】:

    • 我会在早上尝试这个,计算方法看起来很有希望 - 我打算构建类似于它看起来做的东西,我很高兴看到它是否有效。跨度>
    • 计算方法虽然非常有用,但不使用解决“找不到'Window_sendPlatformMessage'(4 个参数)”错误所需的解决方法。我仍然无法使用最简单的包/插件。然而,计算确实减少了很多代码并显着清理了一些东西。
    • 在你实验的时候,你应该只对密集算法使用计算(我仍然不确定输入和输出支持哪种数据,我只测试了简单类型),而不是调用插件或外部包方法,如果您不知道它们在做什么。
    • 我现在看到您说 sqflite 在后台线程上运行 - 您是否知道这是否意味着它使用两组本机代码进行多线程,或者在 dart 中使用隔离?我浏览了自述文件和使用指南,并没有找到关于异步实现的部分,所以我猜你是通过查看代码本身发现的?
    • 它在本机端(iOS 和 Android)使用本机线程。
    猜你喜欢
    • 1970-01-01
    • 2021-03-09
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    相关资源
    最近更新 更多