Для разработчиков, имеющих опыт работы с 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) делают его менее приемлемым.## Различия и эволюция
На самом деле, различия между этими реализациями были подробно рассмотрены в предыдущей статье «Глубокий анализ гибридной композиции».
Обычно, когда вы используете AndroidView
напрямую в Dart-коде, это можно считать использованием режима виртуального отображения (virtual display). Например, в версии 1.2.2 библиотеки flutter_pdfview
. Такое решение заключается в том, что содержимое, которое должно быть отрендерено AndroidView
, рисуется в контексте VirtualDisplays
, а затем изображение рисуется в памяти, связанной с этим VirtualDisplay
, и доступна через его Surface
.
VirtualDisplay
можно рассматривать как виртуальное отображение, которое используется вместе сDisplayManager
. Обычно это применяется в случаях вторичного экрана или записи экрана.VirtualDisplay
рисует содержимое виртуального отображения наSurface
.
Как показано на рисунке выше, вкратце, это означает, что содержимое нативного контроллера рисуется в памяти, а затем Flutter Engine получает данные отрисовки этого контроллера через соответствующий textureId
и отображает их.
Чтобы отладить проблемы, связанные с режимом виртуального отображения, вам может потребоваться следовать такому процессу:
### Гибридная композиция
Использование гибридной композиции будет сложнее с точки зрения кода по сравнению с прямым использованием 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`.
Приведённый ниже пример показывает:
- Два серых "Re" — это нативные `TextView`;
- Синие, жёлтые и красные — это Flutter'овские `Text`.

Из результата отрисовки можно заметить следующее:
- Серый нативный `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
PlatformView
, таких как решение #95343 со скачками экрана. Эта проблема, вероятно, будет решена путём изменения способа отрисовки и оптимизации текстур. Да, и снова из-за проблем с производительностью и другими вопросами, новое реализация PlatformView
возвращается, как можно понять из уже объединённой заявки #31198. Можно предположить, что в следующей стабильной версии текущее реализация виртуальных дисплеев будет отключена, а вместо неё будет использована новая реализация через TextureLayer
. В будущем нельзя исключить отключения и гибридного составления. Интересно, какие чувства вызывают эти изменения?Краткое содержание:- Новый PlatformViewWrapper
заменяет логику, связанную с SurfaceTextureWrapper
в виртуальном дисплее. Он получает Canvas
, используя метод lockHardwareCanvas
для входящего Surface
, а затем выполняет отрисовку через super.draw(surfaceCanvas)
;
Если в будущем реализация PlatformViewWrapper
будет успешной, можно предположить, что модель гибридной композиции также может быть прекращена, поэтому остаётся лишь восхищение скоростью технологического развития Flutter.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )