无法理解“'Window_sendPlatformMessage' (4 arguments) cannot be found”错误的 Flutter Isolate 解决方法
Unable to understand Flutter Isolate workaround for "'Window_sendPlatformMessage' (4 arguments) cannot be found" error
我正在将一个非常简单的标签打印应用程序从 Swift 转换为 Flutter
。此应用程序的一个主要组件是用于离线访问产品的本地缓存数据库。
虽然我可以在主线程上下载并缓存所有 12,000 多个产品,但对于如此繁重的操作来说,这是一个非常糟糕的解决方案,我真的很想避免这种情况,除非万不得已。即使作为最后的手段,如果后台线程无法用于将在线数据缓存到本地数据库等基本任务,我也很难说服我的雇主让我从 Swift 迁移到 Flutter。
因此,我一直在探索 Isolates。在此过程中,每当我在 Isolate
上尝试使用 sqflite and path_provider 缓存数据时,我 运行 就会进入错误 "'Window_sendPlatformMessage' (4 arguments) cannot be found"
。
现在我正在阅读 github and that Isolates don't support the use of plugins (possibly packages?) except in a wonky workaround kind of way. I've tried a plugin,当我尝试使用它时,它只会让我的应用程序崩溃,并显示一个非常神秘的堆栈跟踪,所以看起来解决方法是唯一的方法。
我大约一周前才开始使用 flutter 进行构建,大约两天前才开始使用 Isolates,所以我目前对所有内容的基本了解都很肤浅。在 flutter repo 上,有一个 comment 似乎概述了插件问题的解决方法,我希望它允许我使用 path_provider
和 sqflite
来缓存我正在处理的数据从我的网站获取 API.
更有经验的 Flutter 开发人员能否将此解释分解为婴儿咬伤?
编辑:
正如下面的回答所述,sqflite
已经是异步的,并且显然在与主线程不同的线程上运行,所以看起来我的具体情况将通过对我的飞镖使用计算来解决-纯 API 调用以获取 JSON 数据并使用常规 sqflite
进行存储。尽管如此,仍然需要初学者指南来解决不稳定的 Isolate 解决方法,因此我将问题悬而未决。
我不会准确回答你的问题(如何使用 isolate),但我建议此时不要使用 isolate。 sqflite 在后台线程中运行。是的,准备数据将在主线程中进行,因此绝对不要一次保存 12000 个项目,而是 downloading/saving 个项目 50 个(或 100 或 1000 个,具体取决于每个项目的大小 - 进行实验)交易(甚至更好的批次)应该没问题。
对于大量项目的密集计算(可能包括 json 解码下载的数据或准备地图以保存 sqflite),您可以使用 compute
flutter 方法。
如果您需要显示完整列表(如果需要分页),您可能需要进行一些智能编码,但我认为没有什么可以阻止您做您需要做的事情 'in the background',即不阻塞 UI.
尝试解释
我也发现该线程中解决方法 post 的措辞非常混乱,我也无法真正理解它,但是 poster 似乎是什么建议是您使用 setMockMessageHandler
拦截来自 invokeMethod
.
的数据
setMockMessageHandler
覆盖处理函数,否则处理函数会获取编码数据并将其传递给本机端,从而允许您使用自己的函数。因此,当您为通道设置模拟消息处理程序时,您可以 "hijack" 数据并通过 SendPort
将其传递回您的主隔离区。
一旦您在主隔离区收到消息,您就可以使用 BinaryMessenger.send
将数据发送到其预定目的地。如果您处于主要隔离区以外的隔离区,这一步就会中断,所以现在您已经绕过了这个问题。
一旦您将数据返回到主线程,您将其传递回隔离并使用您使用 BinaryMessages.setMessageHandler
创建的自定义消息处理程序对其进行解码。
解决方法包
如果您仍然需要它,我使用与您询问的解决方法非常相似的解决方法制作了一个程序包 (Isolate Handler)。
当然,任何依赖 setMockMessageHandler
的解决方法的缺点是您确实需要为其提供您将使用的频道名称。您可以通过查找设置 MethodChannel
的位置在源文件中找到这些频道名称。
似乎 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-09-04 更新:
我最终通过从本机端开始隔离来为自己解决了这个问题。我从官方 Flutter 插件 android_alarm_manager. While that plugin is specific to Android and that was enough for my specific project, it is possible to do it in iOS as well as shown in the now-archived location_background_plugin 以及下面引用的 Medium post 中获得灵感。
一位 Flutter 开发人员关于从本机端启动隔离的过程的非常全面(如果有点过分关注地理围栏)post available on Medium。
post中采取的步骤摘要:
- (GitHub) 使用
PluginUtilities.getCallbackHandle
在 Dart 端设置一个引用回调调度程序的插件
- (GitHub) 创建回调调度程序本身,它:
- 初始化您要访问的 MethodChannel。
- 调用
WidgetsFlutterBinding.ensureInitialized()
来设置 MethodChannel
所需的内部状态。
- 在 MethodChannel 上调用
setMethodCallHandler
以侦听来自插件本机端的后台事件,并在事件触发时调用回调。
- 最终使用
invokeMethod
. 提醒本机端插件回调处理程序已准备好处理事件
- 实施本机代码以执行后台执行。
- 设置权限以允许作为服务在后台执行。
- (GitHub) 在应用程序启动时在 Dart 端初始化插件(例如从
main
。)
结论
这绝不是一个简单的解决方案,我仍然希望 Flutter 团队能提供更好的解决方案,但它是一个有效的解决方案,至少没有问题或意外行为。
我认为这是目前唯一可以投入生产的解决方案。
我正在将一个非常简单的标签打印应用程序从 Swift 转换为 Flutter
。此应用程序的一个主要组件是用于离线访问产品的本地缓存数据库。
虽然我可以在主线程上下载并缓存所有 12,000 多个产品,但对于如此繁重的操作来说,这是一个非常糟糕的解决方案,我真的很想避免这种情况,除非万不得已。即使作为最后的手段,如果后台线程无法用于将在线数据缓存到本地数据库等基本任务,我也很难说服我的雇主让我从 Swift 迁移到 Flutter。
因此,我一直在探索 Isolates。在此过程中,每当我在 Isolate
上尝试使用 sqflite and path_provider 缓存数据时,我 运行 就会进入错误 "'Window_sendPlatformMessage' (4 arguments) cannot be found"
。
现在我正在阅读 github and
我大约一周前才开始使用 flutter 进行构建,大约两天前才开始使用 Isolates,所以我目前对所有内容的基本了解都很肤浅。在 flutter repo 上,有一个 comment 似乎概述了插件问题的解决方法,我希望它允许我使用 path_provider
和 sqflite
来缓存我正在处理的数据从我的网站获取 API.
更有经验的 Flutter 开发人员能否将此解释分解为婴儿咬伤?
编辑:
正如下面的回答所述,sqflite
已经是异步的,并且显然在与主线程不同的线程上运行,所以看起来我的具体情况将通过对我的飞镖使用计算来解决-纯 API 调用以获取 JSON 数据并使用常规 sqflite
进行存储。尽管如此,仍然需要初学者指南来解决不稳定的 Isolate 解决方法,因此我将问题悬而未决。
我不会准确回答你的问题(如何使用 isolate),但我建议此时不要使用 isolate。 sqflite 在后台线程中运行。是的,准备数据将在主线程中进行,因此绝对不要一次保存 12000 个项目,而是 downloading/saving 个项目 50 个(或 100 或 1000 个,具体取决于每个项目的大小 - 进行实验)交易(甚至更好的批次)应该没问题。
对于大量项目的密集计算(可能包括 json 解码下载的数据或准备地图以保存 sqflite),您可以使用 compute
flutter 方法。
如果您需要显示完整列表(如果需要分页),您可能需要进行一些智能编码,但我认为没有什么可以阻止您做您需要做的事情 'in the background',即不阻塞 UI.
尝试解释
我也发现该线程中解决方法 post 的措辞非常混乱,我也无法真正理解它,但是 poster 似乎是什么建议是您使用 setMockMessageHandler
拦截来自 invokeMethod
.
setMockMessageHandler
覆盖处理函数,否则处理函数会获取编码数据并将其传递给本机端,从而允许您使用自己的函数。因此,当您为通道设置模拟消息处理程序时,您可以 "hijack" 数据并通过 SendPort
将其传递回您的主隔离区。
一旦您在主隔离区收到消息,您就可以使用 BinaryMessenger.send
将数据发送到其预定目的地。如果您处于主要隔离区以外的隔离区,这一步就会中断,所以现在您已经绕过了这个问题。
一旦您将数据返回到主线程,您将其传递回隔离并使用您使用 BinaryMessages.setMessageHandler
创建的自定义消息处理程序对其进行解码。
解决方法包
如果您仍然需要它,我使用与您询问的解决方法非常相似的解决方法制作了一个程序包 (Isolate Handler)。
当然,任何依赖 setMockMessageHandler
的解决方法的缺点是您确实需要为其提供您将使用的频道名称。您可以通过查找设置 MethodChannel
的位置在源文件中找到这些频道名称。
似乎 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-09-04 更新:
我最终通过从本机端开始隔离来为自己解决了这个问题。我从官方 Flutter 插件 android_alarm_manager. While that plugin is specific to Android and that was enough for my specific project, it is possible to do it in iOS as well as shown in the now-archived location_background_plugin 以及下面引用的 Medium post 中获得灵感。
一位 Flutter 开发人员关于从本机端启动隔离的过程的非常全面(如果有点过分关注地理围栏)post available on Medium。
post中采取的步骤摘要:
- (GitHub) 使用
PluginUtilities.getCallbackHandle
在 Dart 端设置一个引用回调调度程序的插件
- (GitHub) 创建回调调度程序本身,它:
- 初始化您要访问的 MethodChannel。
- 调用
WidgetsFlutterBinding.ensureInitialized()
来设置MethodChannel
所需的内部状态。 - 在 MethodChannel 上调用
setMethodCallHandler
以侦听来自插件本机端的后台事件,并在事件触发时调用回调。 - 最终使用
invokeMethod
. 提醒本机端插件回调处理程序已准备好处理事件
- 实施本机代码以执行后台执行。
- 设置权限以允许作为服务在后台执行。
- (GitHub) 在应用程序启动时在 Dart 端初始化插件(例如从
main
。)
结论
这绝不是一个简单的解决方案,我仍然希望 Flutter 团队能提供更好的解决方案,但它是一个有效的解决方案,至少没有问题或意外行为。
我认为这是目前唯一可以投入生产的解决方案。