1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Flutter-shorebird.md 27 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Подробнее о самом официальном варианте горячей доставки в Flutter: Shorebrid

Горячая доставка всегда была одной из самых популярных тем в Flutter, ведь его "врожденные свойства" делают его отличным от RN, который имеет широко используемое решение типа Code Push. Однако за годы развития Flutter также создал несколько путей для горячей доставки, таких как:

  • Использование JS/TS для создания компонентов для динамической отправки обновлений;
  • Использование шаблонов Vue для динамической отправки обновлений;
  • Использование JSON-конфигураций для статического UI с последующими обновлениями;
  • Обработка процесса компиляции Dart и DSL для динамической отправки обновлений;
  • Предварительная установка шаблонов компонентов для динамических обновлений;
  • ...

Каждый из этих методов имеет свои преимущества и недостатки в различных сценариях использования. Сегодня мы поговорим о Shorebrid, которая является уникальной благодаря тому, что это коммерческий проект бывшего основателя Flutter Эрика Шмидта. На данный момент Shorebrid является наиболее близким аналогом RN Code Push в мире Flutter.

Существуют три ключевых аспекта, которые обычно рассматриваются при обсуждении горячей доставки в Flutter:

  • Влияет ли она на производительность?;

  • Соответствует ли она нормативным требованиям?;

  • Какова стоимость внедрения и удаления?Рассматривая эти аспекты, можно сделать следующие выводы относительно Shorebrid:

  • Производительность на платформе Android остаётся неизменной, на iOS же может немного снижаться в зависимости от ситуации;

  • Способ доставки полностью соответствует нормативным требованиям;

  • Легко внедряться и легко выключаться одним нажатием кнопки;

Как же Shorebrid достигает этих целей с точки зрения технологии? Это связано с модификацией Flutter Engine и Dart VM, которую можно рассматривать как частичную форк Flutter.

Проще говоря, вам потребуется заменить команду flutter build на shorebird build, когда вы будете строить свой проект Flutter. Некоторые могут считать такой подход слишком вторгающимся или дорогостоящим для внедрения, особенно если вы замените даже командную строку (CLI).

Однако это не совсем так. При использовании Shorebrid вы всё ещё можете использовать оригинальные команды flutter build и flutter run для повседневного разработки и сборки вашего проекта.> Это возможно благодаря кэшированию директорий Shorebird, которые содержат собственные версии Flutter lib, которые можно легко разделить от локальных версий Flutter, обеспечивая соответствие версий, например, через команду shorebird flutter versions list. Для Shorebird вам обычно требуется вмешиваться только при выпуске или сборке патча. В остальное время работа с Flutter ничем не отличается от обычной разработки. Это связано с тем, что Shorebird, хотя и модифицировал Engine и VM, но не внес изменений, нарушающих функциональность, а лишь добавил новые возможности, что позволяет легко переключаться между Flutter и Shorebird.> Даже "модификация" Flutter Engine в Shorebrid составляет всего несколько сотен строк кода, а основная часть работы связана с кастомизацией логики Dart VM.

Как же Shorebrid осуществляет горячую замену? Ответ заключается в распространении бинарного файла патча. Возможно, вы удивитесь, узнав, что распространение бинарного файла может быть законным. Мы объясним это позже.

Сначала давайте рассмотрим реализацию Shorebrid. В случае отсутствия патчей для обновления, Shorebrid ничем не отличается от официального Flutter, то есть его производительность аналогична производительности оригинального Flutter.

Однако, когда существует патч для горячего обновления, ситуация различается в зависимости от платформы:

  • Производительность Android не изменится, так как Shorebrid просто заменяет бинарный файл на Android. Shorebrid сообщает Dart VM загружать различные версии Dart-кода во время выполнения.
  • Производительность iOS будет отличаться, поскольку для патчей на iOS требуется интерпретация, тогда как незапятнанные части продолжают выполнять AOT (Ahead-of-Time compilation).

Пример: начнем с Android. После использования команды shorebird build для сборки Android, CLI отправляет собранную версию на централизованную платформу управления Shorebrid:

Изображение

Затем, при создании нового патча, Shorebrid скачивает вашу версию и сравнивает её, чтобы создать минимальный бинарный патч для распространения:Изображение

Если вы посмотрите на этот файл, вы заметите, что он представляет собой бинарный файл размером от нескольких десятков КБ до нескольких сотен КБ, называемый dlc.vmcode:

Изображение Изображение

На Android Shorebrid использует распространение этого файла для замены соответствующих частей в Dart VM, обеспечивая поддержку динамического обновления. Поэтому на Android Shorebrid всегда работает в режиме AOT, и его производительность практически не изменяется. Так почему в Android можно использовать такой способ обновлений? Это связано с политикой Google Play, как показано ниже:

image

Ограничение не применяется к коду, который выполняется в виртуальной машине или интерпретаторе.

Это значит, что хотя Shorebird выпускает AOT-биндинги, они не могут непосредственно запускаться на Android или JVM; им требуется Dart VM для выполнения.> Здесь может возникнуть вопрос: если код уже скомпилирован в машинночитаемый формат, зачем нужна виртуальная машина? Ответ заключается в том, что система не предоставляет соответствующую среду выполнения. AOT-код Dart также требует библиотек и среды выполнения для сборки мусора. Например, после компиляции C-кода он всё ещё зависит от реализации стандартной библиотеки C, а SO-библиотеки могут использовать упрощенную версию libstdc++, предоставленную Android.Но это не применимо к iOS, поскольку любое исполняемое кодовое дерево на App Store не допускает динамической загрузки. Поэтому при создании горячих обновлений разработчики iOS вынуждены использовать интерпретаторы. Однако полное использование интерпретатора приведёт к крайне низкой производительности приложения. В результате Shorebird на iOS реализован таким образом, чтобы неизменённый код продолжал выполнять AOT на процессоре, а изменённый патч-код выполнялся через интерпретатор Shorebird до Dart VM.

image

Такое решение позволяет максимально приблизить производительность Flutter на iOS к её оригинальному уровню. Если ваш проект не имеет патчей, то производительность не будет снижена. При наличии патчей производительность будет снижена только во время выполнения этих патчей. Это также затрагивает "модифицированную" поддержку Dart VM: Shorebird добавил интерпретатор и новый линкер в VM.

Интерпретатор, конечно же, анализирует код, позволяя ему работать в режиме JIT на Dart VM, благодаря тому, что Dart сам по себе имеет два рабочих режима — JIT и AOT. Кроме того, Dart обычно хранит информацию о исходном коде даже в режиме JIT, которая является ключевой для оптимизации горячего кода. Эта информация также важна для того, чтобы Shorebird мог обеспечивать "гибридный режим" работы Dart VM.А здесь Linker может отличаться от понятия линковщика, которое мы используем на iOS. Он не подписывает код, он больше используется для анализа конкретных функций при генерации патч-файлов, чтобы определить, следует ли выполнять эти функции напрямую на процессоре. Ранее уже упоминалось, что iOS запрещает динамическую отправку любых исполняемых файлов, поэтому Shorebird не будет отправлять ничего, что можно было бы непосредственно выполнить на процессоре (включая даже JIT-файлы после компиляции Dart) (что также связано с вопросами подписи). Роль Linker заключается в анализе существующих AOT-файлов, сравнении снимков различий на уровне команд и сохранении неизменной части.

Linker здесь выступает как штопальщик, основная задача которого — выделение функций, которые нельзя выполнять на процессоре, а затем их отделение для последующего выполнения через интерпретатор.

Здесь стоит сделать отступление и объяснить, почему JIT-файлы после компиляции Dart тоже недопустимы? С версии Dart 2.0 Dart VM больше не может непосредственно интерпретировать исходный код Dart; теперь ему требуется сериализованная AST-структура ядра (файл dill в двоичном формате). Исходный код Dart преобразуется в AST-структуру ядра с помощью переднего конца CFE:

> Двоичный формат не обязательно является исполняемым машинным кодом.

В режиме JIT Flutter не выполняет прямое парсинг исходного кода Dart, а работает вместе с другим процессом frontend_server, обеспечивая известный эффект горячей загрузки. После загрузки двоичного файла ядра в VM, все связанные с программой объекты (классы, объекты) загружаются лениво: сначала загружается базовая информация о библиотеках и классах, а полная десериализация информации о классах происходит только при необходимости во время выполнения программы:

Это также вызывает проблему с работой Flutter Debug на iOS 18.4 beta, поскольку система больше не позволяет незаконченным по подписке двоичным файлам быть исполнены через JIT. Раньше это было возможно, так как это было "безопасной уязвимостью", позволяющей разработчикам обходить некоторые требования к подписям на реальных устройствах. Теперь метод манипулирования правами доступа памяти в реальном времени (mprotect) больше не поддерживается.Поэтому, JIT в Dart отличается от обычного интерпретируемого выполнения кода, и это также создает заблуждение в сообществе Flutter, где считается, что плохие производственные характеристики в режиме отладки связаны именно с JIT. Однако это не совсем верно. Основной причиной замедления является наличие множества проверок согласованности/утвержденний в рамках фреймворка Flutter, которые вызывают значительное снижение производительности и активируются только в режиме отладки.> JIT также может работать медленнее, но не так значительно, как при выполнении flutter run debug.

Главное отличие JIT-режима заключается в необходимости "разогрева", поэтому программа может потребовать некоторого времени для достижения максимальной производительности и будет использовать больше памяти. Однако с точки зрения теоретического пика производительности, он не проигрывает AOT.

AOT же характеризуется быстрой загрузкой и моментальным достижением максимальной производительности без необходимости разогрева, что делает его идеальным выбором для сценариев с пользовательским интерфейсом, где JIT-непредсказуемость и время разогрева недопустимы.

Итак, вернемся к горячему обновлению iOS через Shorebridge:

  • Без наличия патча обновление полностью работает в режиме AOT;
  • При наличии патча обновление части кода требует интерпретируемого выполнения, что приводит к снижению производительности;
  • Теоретически, если обновляемый раздел представляет собой UI, то различия в производительности могут быть более заметными, особенно при работе с рендерингом изображений;

Конечно, в целом, даже при наличии патчей, производительность остается близкой к максимуму — свыше Yöntemlerinde %90, поскольку большинство случаев использования горячего обновления связано с небольшим количеством кода, требующего изменения.Поскольку наиболее затратные операции, такие как вычисление макета, выполняются в AOT на уровне процессора, а необходимые изменения обычно незначительны, поскольку крупные изменения лучше всего реализуются через обновление платформы с точки зрения регламентированности:

image

Поэтому, в контексте iOS, простое объяснение можно представить следующим образом:

VM Dart имеет два снимка: один подписанный и готовый к выполнению на процессоре; второй — это снимок патча, который не может быть выполнен напрямую на процессоре и требует интерпретатора, который действует как псевдо-процессор.Это и есть причина модификаций Engine в Shorebird, чтобы позволить запуск обновленного кода во время выполнения с помощью VM Dart и поддерживать работу интерпретатора в режиме производства. При запуске в режиме JIT Dart-функции могут иметь различные компилированные формы представлений, такие как простое компилирование и оптимизированное компилирование для одной и той же функции. Shorebird использует эту особенность архитектуры Dart, внедряя новый интерпретатор в качестве альтернативного механизма выполнения функций, что позволяет эффективно заменять некоторые части приложения во время выполнения без необходимости компилировать новую версию кода на устройстве. Dart иногда переключает выполнение с ненооптимизированного кода на оптимизированный во время выполнения функции:

На уровне кода можно заметить, что при инициализации Flutter движок встраивается в инициализацию объекта ConfigureShorebird, который является источником всех "волшебных" действий:

Цель конфигурации заключается в основном в чтении информации из файла патча, таких как классы vm_snapshot и isolate_snapshot, глобальные переменные, указатели на функции, стек, команды и т.д.:

Например, если вместо App.framework/App используется foo.vmcode на iOS, то из него нужно извлечь символы и загрузить их в статические переменные, сохраняя их. Также можно отметить, что файл vmcode как патч — это ELF-файл с префиксом заголовка связывания shorebird:

Итоговый патч становится FileCallbacks после вызова shorebird_init, затем управление переходит к Rust-коду обновителя:

В Shorebird обновитель написан на Rust и предназначен для обновления и управления патчем в Flutter. Он компилируется как статическая библиотека, например, при сборке для Android она связывается с движком Flutter (libflutter.so).Конечно, после применения патча выполнение происходит в модифицированной версии Dart VM, которая является сердцем Shorebird. Однако к сожалению, эта часть пока не доступна для общего использования.Таким образом, мы теперь имеем полное представление о реализации Shorebird, и нам следует обсудить его ограничения.

  • Сначала Shorebird не может обновлять никакие native коды, так как обновление native кодов недопустимо. Цель Shorebird — исправление Dart кода.
  • Нельзя использовать патчи между версиями Flutter, даже если это мелкие версии, поскольку Shorebird использует уже выпущенные файлы приложения для получения минимального различия патча. Поэтому рекомендуется каждый раз строить Flutter в одной и той же фиксированной версии.
  • Минимально поддерживаемая версия должна быть хотя бы 3.10:
    • Android требует Flutter 3.10.0 или выше.
    • iOS требует Flutter 3.24.0 или выше.
    • macOS требует Flutter 3.27.3 или выше.
    • Windows требует Flutter 3.27.2 или выше.
  • Проблемы с устойчивостью сервера, так как Shorebird теперь использует CDN CloudFlare, иногда в некоторых специфических регионах всё ещё возникают проблемы со стабильностью.

На самом деле до этого момента Shorebird был хостингом на Google, но из-за настоятельных требований китайских пользователей было принято решение о миграции на CloudFlare, даже на Discord есть отдельный китайский раздел.

  • Поддержка автономной установки недоступнаКроме того, самое страшное при использовании такого фреймворка — это скорость обновлений версий Flutter, однако для Shorebird это практически не является проблемой, поскольку скорость обновления версий Flutter у Shorebird почти синхронизирована, а даже последний переход Flutter на monorepo также был быстро синхронизирован, поэтому здесь нет необходимости беспокоиться.Наконец, механизмы выхода из Shorebird практически незаметны: если вы больше не хотите использовать его, достаточно удалить аккаунт и переключиться обратно на flutter build.

Ссылки для справки

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master