Невозможно понять обходной путь Flutter Isolate для «Window_sendPlatformMessage» (4 аргумента), ошибка не найдена.

Я конвертирую очень упрощенное приложение для печати этикеток на Flutter из Swift. Основным компонентом этого приложения являются локально кэшированные базы данных для автономного доступа к продуктам.

Хотя я мог загружать и кэшировать все 12 000+ продуктов в основном потоке, это ужасное, ужасное решение для такой тяжелой операции, и я бы очень хотел избежать этого, кроме как в крайнем случае. Даже в крайнем случае у меня было бы гораздо больше проблем с тем, чтобы убедить моего работодателя разрешить мне перейти со Swift на Flutter, если фоновая потоковая передача невозможна для базовой задачи, такой как кэширование онлайн-данных в локальную базу данных.

В результате я исследовал Изоляты. В процессе я сталкивался с ошибкой "'Window_sendPlatformMessage' (4 arguments) cannot be found" всякий раз, когда пытался кэшировать данные с помощью sqflite и path_provider на Isolate.
Сейчас я читаю на github и stackoverflow, что Isolates не поддерживает использование плагинов (возможно, пакетов?), кроме как в шаткий способ обходного пути. Я пробовал плагин, который просто вылетает из моего приложения с очень загадочной трассировкой стека, когда я пытаюсь использовать это, так что похоже, что обходной путь - единственный способ.

Я только начал создавать с флаттером около недели назад и только два дня назад начал работать с Isolates, так что мое базовое понимание всего в настоящее время поверхностное. В репозитории флаттера есть комментарий, который, кажется, описывает обходной путь для проблемы с плагином, который, я надеюсь, позволит мне использовать path_provider и sqflite для кэширования данных, которые я получаю из своего веб-API.

Может ли более опытный разработчик Flutter разбить это объяснение на детские укусы?


изменить:

Как отмечено в ответе ниже, sqflite уже является асинхронным и, по-видимому, работает в другом потоке, чем основной, поэтому похоже, что моя конкретная ситуация будет решена путем использования вычислений для моего вызова API-интерфейса dart-pure для получения данных JSON и использования обычный sqflite для хранения. Тем не менее потребность в руководстве для начинающих по нестандартным обходным путям Isolate сохраняется, поэтому я оставляю вопрос открытым.


person A. L. Strine    schedule 12.08.2019    source источник


Ответы (2)


Попытка объяснения

Я тоже нахожу формулировку сообщения об обходном пути в этой ветке очень запутанной, и я тоже не могу полностью понять ее, однако плакат, похоже, предлагает вам использовать 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-09-04:

В итоге я решил эту проблему для себя, запустив изоляцию с родной стороны. Я черпал вдохновение из официального плагина Flutter android_alarm_manager. Хотя этот плагин специфичен для Android, и этого было достаточно для моего конкретного проекта, это можно сделать в iOS, а также показано в архивированном location_background_plugin, а также сообщение Medium, указанное ниже.

Очень исчерпывающий (хотя и слишком сосредоточенный на Geofencing) пост одного из разработчиков Flutter о процессе инициализации изолятов с нативной стороны: доступно на Medium.

Сводка шагов, предпринятых в сообщении:

  1. (GitHub) Настройте плагин, ссылающийся на диспетчер обратного вызова сторона с использованием PluginUtilities.getCallbackHandle
  2. (GitHub) Create the callback dispatcher itself, which:
    • Initializes the MethodChannel(s) you want to access.
    • Вызывает WidgetsFlutterBinding.ensureInitialized() для установки внутреннего состояния, необходимого для MethodChannel.
    • Вызывает setMethodCallHandler в MethodChannel (s) для прослушивания фоновых событий с собственной стороны вашего плагина и вызывает ваш обратный вызов, когда события запускают его.
    • Наконец, предупреждает встроенный боковой плагин, что обработчик обратного вызова готов к событиям, используя invokeMethod.
  3. Implement native code to perform background execution.
  4. Set permissions to allow background execution as a service.
    • Android
      • (GitHub) Register plugin objects with AndroidManifest.xml as service.
      • (GitHub) Создайте собственный FlutterActivity.
      • (GitHub) Обновите поле application в AndroidManifest.xml, установите новое создано FlutterActivity.
    • iOS
      • (GitHub). Modify Info.plist to request permissions
      • (GitHub) Установите ссылку на регистранта плагина приложения из AppDelegate.
  5. (GitHub) Инициализировать плагин на стороне Dart при запуске приложения (например, с main.)

Заключение

Это ни в коем случае не простое решение, и я все еще надеюсь, что будет лучшее решение, предоставленное командой Flutter, но оно работает, по крайней мере, без сбоев или неожиданного поведения.

Я считаю, что это единственное решение, готовое к производству на данный момент.

person Krista    schedule 26.08.2019
comment
Интересно. Однако sqflite не является безопасным для перекрестной изоляции транзакций, поэтому вы можете получить поврежденные данные при одновременной записи, поэтому я не уверен, что это безопасное решение (в настоящее время). - person alextk; 28.08.2019
comment
Это выглядит многообещающе! Возможно, я не смогу протестировать его в течение нескольких недель, поэтому я проголосовал за, но пока не отмечаю ответ. Как только у меня будет возможность протестировать, я дам вам знать, как это сработало для меня. - person A. L. Strine; 04.09.2019
comment
@ A.L.Strine В итоге я нашел лучшее решение для своего собственного проекта, вместо этого запустив изоляцию с нативной стороны. Я обновлю свой пост. - person Krista; 04.09.2019

Я не буду точно отвечать на ваш вопрос (как использовать изолировать), но я бы рекомендовал не использовать изоляцию на данном этапе. sqflite работает в фоновом потоке. Да, подготовка данных будет в основном потоке, поэтому вам определенно никогда не следует сохранять 12000 элементов за раз, а загружать / сохранять элементы на 50 (или 100 или 1000 в зависимости от размера каждого элемента - для экспериментов) в транзакции ( даже лучше партия) должно быть в порядке.

Для интенсивных вычислений на большом количестве элементов (может включать декодирование загруженных данных в формате json или подготовку карт для сохранения в sqflite) вы можете использовать метод compute flutter.

Это может потребовать некоторого умного кодирования на вашей стороне, если вам нужно отобразить полный список (при необходимости разбить на страницы), но я думаю, что ничто не должно мешать вам делать то, что вам нужно, «в фоновом режиме», то есть без блокировки пользовательского интерфейса.

person alextk    schedule 13.08.2019
comment
Я попробую это утром, метод вычислений выглядит очень многообещающим - я собирался создать что-то похожее на то, что он, кажется, делает, я очень рад увидеть, работает ли он. - person A. L. Strine; 14.08.2019
comment
Метод вычисления, хотя и очень полезен, НЕ использует обходной путь, необходимый для устранения ошибки «Window_sendPlatformMessage» (4 аргумента) не может быть найден. Я до сих пор не могу использовать даже самые простые пакеты / плагины. Однако вычислительные ресурсы действительно сократили объем кода и значительно очистили его. - person A. L. Strine; 14.08.2019
comment
По мере того, как вы экспериментировали, вы должны использовать вычисления только для интенсивного алгоритма (я все еще не уверен, какие данные поддерживаются для ввода и вывода, я тестировал только с простыми типами), а не для вызова плагинов или методов внешних пакетов, если вы этого не сделаете. не знаю, что они делают. - person alextk; 14.08.2019
comment
Теперь я вижу, что вы сказали, что sqflite работает в фоновом потоке - знаете ли вы, означает ли это, что он использует два набора собственного кода для многопоточности или изоляцию в дротике? Я бегло просмотрел readme и руководство по использованию и не нашел раздела, посвященного реализации async, так что я предполагаю, что вы узнали это, посмотрев на сам код? - person A. L. Strine; 15.08.2019
comment
Я предполагал, что реализация async была через фьючерсы - person A. L. Strine; 15.08.2019
comment
Он использует собственный поток на собственной стороне (iOS и Android). - person alextk; 16.08.2019