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

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

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

Для разработчиков, имеющих опыт работы с Flutter, использование native компонентов через PlatformView в гибридной разработке должно быть знакомо. Тем, кто начал использовать Flutter до версии 1.20, наверняка пришлось столкнуться со множеством проблем, связанных с использованием PlatformView на платформе Android, такими как проблема с появлением клавиатуры внутри WebView.

⚠️ Внимание: В конце статьи вас ждет приятный сюрприз.

Начнем с одной проблемы

Недавно один друг столкнулся с такой проблемой при тестировании webview_flutter и flutter_pdfview на Flutter 2.10.1:

attachToContext: GLConsumer уже привязан к контексту,
android.graphics.SurfaceTexture.attachToGLContext(SurfaceTexture.java:289)

Итак, мы воспользуемся этой проблемой, чтобы рассказать вам о том, как реализация PlatformView в Flutter эволюционировала и что нас ждет в будущем. Проблема возникла из-за того, что:

смешивание двух реализаций PlatformView: virtual displays и hybrid composition.

С момента выпуска Flutter 2.10 официальные плагины, такие как webview_flutter, используют новую реализацию hybrid composition. Однако сторонние библиотеки, такие как flutter_pdfview, все ещё используют старый метод virtual display. Это привело к ситуации, когда две разных реализации PlatformView работали одновременно.Конечно, эта проблема была исправлена в версии 2.10.2 #31390. Причина заключалась в следующем: когда задачи растрирования выполняются в разных потоках, объект GrContext создаётся заново, что приводит к тому, что текстура остаётся неинициализированной, а повторное вызов attachToGLContext приводит к ошибке.> Поэтому в последующих исправлениях перед вызовом attachToGLContext проверяется состояние текстуры, если она уже была привязана, то сначала вызывается detachFromGLContext для освобождения, что позволяет избежать повторной инициализации контекста.

Однако анализируя проблему, можно сделать вывод, что это не было уникальной проблемой для версии 2.10; она могла возникать всякий раз, когда объект SurfaceTextureWrapper существовал и использовались вместе виртуальные отображения и гибридная композиция.

SurfaceTextureWrapper используется официально для решения вопроса синхронизации, поскольку вызов SurfaceTexture.release происходит в потоке платформы, тогда как attachToGLContext вызывается в потоке растрирования. Различие в потоках может привести к тому, что при вызове attachToGLContext текстура уже была освобождена, поэтому нужна SurfaceTextureWrapper для обеспечения синхронизации на уровне Java. Если в более ранних версиях вы не хотите обновляться, то можете выбрать использование всех плагинов в режиме виртуального отображения (virtual display) или гибридной композиции (hybrid composition). Например, плагин webview_flutter предоставляет WebView.platform, чтобы пользователи могли свободно выбирать режим отрисовки PlatformView.Конечно, обычно я рекомендую использовать режим гибридной композиции (hybrid composition), хотя оба режима имеют потенциальные проблемы. Однако текущие проблемы производительности и работы с клавиатурой при использовании режима виртуального отображения (virtual display) делают его менее приемлемым.## Различия и эволюция

На самом деле, различия между этими реализациями были подробно рассмотрены в предыдущей статье «Глубокий анализ гибридной композиции».

VirtualDisplay

Обычно, когда вы используете AndroidView напрямую в Dart-коде, это можно считать использованием режима виртуального отображения (virtual display). Например, в версии 1.2.2 библиотеки flutter_pdfview. Такое решение заключается в том, что содержимое, которое должно быть отрендерено AndroidView, рисуется в контексте VirtualDisplays, а затем изображение рисуется в памяти, связанной с этим VirtualDisplay, и доступна через его Surface.

VirtualDisplay можно рассматривать как виртуальное отображение, которое используется вместе с DisplayManager. Обычно это применяется в случаях вторичного экрана или записи экрана. VirtualDisplay рисует содержимое виртуального отображения на Surface.

Как показано на рисунке выше, вкратце, это означает, что содержимое нативного контроллера рисуется в памяти, а затем Flutter Engine получает данные отрисовки этого контроллера через соответствующий textureId и отображает их.

Чтобы отладить проблемы, связанные с режимом виртуального отображения, вам может потребоваться следовать такому процессу:

image-20220305161230961### Гибридная композиция

Использование гибридной композиции будет сложнее с точки зрения кода по сравнению с прямым использованием AndroidView. Для этого потребуется использовать объекты PlatformViewLink, AndroidViewSurface и PlatformViewsService. В первую очередь нам следует создать один Dart-компонент:```dart // Ваш Dart-контроллер здесь


Продолжайте следовать указаниям для создания вашего приложения Flutter. Через `viewType` в `PlatformViewLink` был зарегистрировано имя, соответствующее нативному слою; это аналогично регистрации `PlatformView`, которую использовали ранее;
Затем в методе `surfaceFactory` возвращается объект `AndroidViewSurface`, который используется для отрисовки и получения событий касаний;
Наконец, в методе `onCreatePlatformView` с помощью `PlatformViewsService` инициализируется `AndroidViewSurface` вместе со всеми необходимыми параметрами, а также через `Engine` запускается отображение нативного слоя.

```dart
Widget build(BuildContext context) {
  // Это используется на стороне платформы для регистрации представления.
  final String viewType = 'hybrid-view-type';
  // Передаются параметры на сторону платформы.
  final Map<String, dynamic> creationParams = <String, dynamic>{};

  return PlatformViewLink(
    viewType: viewType, 
    surfaceFactory:
        (BuildContext context, PlatformViewController controller) {
      return AndroidViewSurface(
        controller: controller,
        gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
        hitTestBehavior: PlatformViewHitTestBehavior.opaque,
      );
    },
    onCreatePlatformView: (PlatformViewCreationParams params) {
      return PlatformViewsService.initSurfaceAndroidView(
        id: params.id,
        viewType: viewType,
        layoutDirection: TextDirection.ltr,
        creationParams: creationParams,
        creationParamsCodec: StandardMessageCodec(),
      )
        ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
        ..create();
    },
  );
}

Если сделать прямое сравнение с вышеописанным примером, то изменения будут следующими:


При использовании *гибкой композиции*, **`PlatformView`** добавляется к **`FlutterView`** через **`FlutterMutatorView`** с использованием нативного контроллера **`addView`**. Затем, чтобы смешивать слои, используется возможность **`FlutterImageView`****, что можно объяснить так:

> Flutter использует нативный метод `addView`, чтобы добавить `PlatformView` к `FlutterView`, что позволяет избежать затрат на рендеринг поверхности. Когда требуется отрисовать свои виджеты внутри `PlatformView`, Flutter добавляет ещё один `FlutterImageView`.

![img](http://img.cdn.guoshuyu.cn/20220328_Flutter-HV/image4)Приведённый ниже пример показывает:

- Два серых "Re" — это нативные `TextView`;
- Синие, жёлтые и красные — это Flutter'овские `Text`.

![img](http://img.cdn.guoshuyu.cn/20220328_Flutter-HV/image5)

Из результата отрисовки можно заметить следующее:

- Серый нативный `TextView` добавляется в `FlutterView` через `PlatformView`, используя метод `addView` нативной системы;
- Красный Flutter'овский `Text` контролируется обычной логикой отрисовки Flutter, так как он не связан с `PlatformView`;
- Жёлтые и синие Flutter'овские контролы, имеющие отношение к `PlatformView`, отображаются через новый `FlutterImageView`.

При использовании *гибридной композиции*, при отправке кадра (`SubmitFrame`) в Engine, используется `current_frame_view_count` для планирования каждого кадра. Затем проверяется необходимость создания поверхности (`CreateSurfaceIfNeeded`), что приводит к вызову нативного метода `createOverlaySurface` для создания `FlutterImageView`.```c++
    for (const SkRect& overlay_rect : overlay_layers.at(view_id)) {
      std::unique_ptr<SurfaceFrame> frame = 
          CreateSurfaceIfNeeded(context,               //
                                 view_id,               //
                                 pictures.at(view_id),  //
                                 overlay_rect           //
          );
      if (should_submit_current_frame) {
        frame->Submit();
      }
    }

Для отладки функциональности гибридной композиции можно обратиться к следующему пути. Отличие от виртуального дисплея заключается в том, что путь меняется после создания. Подробнее см.: https://juejin.cn/post/6858473695939084295#heading-2

image-20220305165318255

image-20220305141848256

ЗаключениеКак видно, гибридная композиция позволяет сохранять большую часть эффектов нативных контроллеров и экономит затраты на отрисовку. Однако существуют несколько острых проблем с PlatformView, таких как решение #95343 со скачками экрана. Эта проблема, вероятно, будет решена путём изменения способа отрисовки и оптимизации текстур. Да, и снова из-за проблем с производительностью и другими вопросами, новое реализация PlatformView возвращается, как можно понять из уже объединённой заявки #31198. Можно предположить, что в следующей стабильной версии текущее реализация виртуальных дисплеев будет отключена, а вместо неё будет использована новая реализация через TextureLayer. В будущем нельзя исключить отключения и гибридного составления. Интересно, какие чувства вызывают эти изменения?

Краткое содержание:- Новый PlatformViewWrapper заменяет логику, связанную с SurfaceTextureWrapper в виртуальном дисплее. Он получает Canvas, используя метод lockHardwareCanvas для входящего Surface, а затем выполняет отрисовку через super.draw(surfaceCanvas);

  • На данный момент гибридная композиция выглядит как переименование, если основной логике не были внесены значительные изменения;

Если в будущем реализация PlatformViewWrapper будет успешной, можно предположить, что модель гибридной композиции также может быть прекращена, поэтому остаётся лишь восхищение скоростью технологического развития Flutter.

Опубликовать ( 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