Реализация PlatformView в кросс-платформенной разработке всегда была актуальной темой. В предыдущей статье "Глубинное сравнение реализации PlatformView в Flutter и Compose" мы подробно рассмотрели различия между Flutter и Compose в реализации PlatformView, а также причины того, почему Compose имеет преимущество при сравнимых реализациях.
С выходом версии 3.29 Flutter начал внедрять другую реализацию PlatformView для Android, которая обеспечивает лучшую производительность по сравнению с HC и временно называется HCPP.
Перед тем как говорить о HCPP, давайте вспомним модели реализации PlatformView в Flutter для Android:
VirtualDisplay
, чтобы эмулировать отрисовку нативных компонентов и извлечение текстур в памяти.FlutterView
и использование нового FlutterImageView
для предоставления поверхности Surface
для сложной стековой композиции UI.FlutterView
, но с использованием родительского канваса вместо дочернего, что позволяет нативному компоненту рисовать на surface.lockHardwareCanvas
.Может показаться абстрактным? Но дальше будут простые примеры для наглядности.Каждый из этих подходов имеет свои преимущества и недостатки, например:
SurfaceView
, так как они имеют собственные поверхности Surface
и канвы Canvas
, которые напрямую связаны с SurfaceFlinger
.TextureView
или базирующемся на OpenGL рендере), требуется явное вызов invalidate
для соответствующего PlatformView при каждом обновлении содержимого, например, вызов mapView.invalidate
внутри метода textureView.onSurfaceTextureUpdated
.Поэтому эти три модели работают совместно, например:
initAndroidView
: По умолчанию использует последнюю модель, то есть TLHC, и понижается до VD при необходимости!
initSurfaceAndroidView
: По умолчанию использует последнюю модель, то есть TLHC, и понижается до HC при необходимости!
initExpensiveAndroidView
: Принудительно использует режим HCТеперь, вернувшись к теме нашего обсуждения, в связи с новой реализацией HCPP, Flutter предоставляет новый API initHybridAndroidView
. Как видно, он требует среду с поддержкой Vulkan и API 34, поэтому его универсальность относительно низка.
Почему же он требует API 34? Это связано с прямым использованием API SurfaceControl
, а также из логики проверки движка можно заметить, что помимо проверки на наличие Vulkan и API, требуется настройка соответствующего EnableSurfaceControl
, то есть добавление следующих метаданных в AndroidManifest
:
<meta-data
android:name="io.flutter.embedding.android.EnableSurfaceControl"
android:value="true" />
Далее давайте рассмотрим различия между HCPP и другими режимами, особенно сравним его с HC и TLHC. В качестве примера создадим контейнер Demo, который будет демонстрировать смешивание Flutter и native компонентов. platformView
будет отображаться между двумя Flutter виджетами:
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Stack(
alignment: AlignmentDirectional.center,
children: <Widget>[
/// Зелёный Flutter прямоугольник размером 200x200
TextButton(
key: const ValueKey<String>('AddOverlay'),
onPressed: _togglePlatformView,
child: const SizedBox(width: 190, height: 190, child: ColoredBox(color: Colors.green)),
),
/// Контейнер native размером 200x200, здесь используется красный native прямоугольник
if (showPlatformView) ...<Widget>[
SizedBox(width: 200, height: 200, child: widget.platformView),
/// Жёлтый Flutter прямоугольник
TextButton(
key: const ValueKey<String>('RemoveOverlay'),
onPressed: _togglePlatformView,
child: const SizedBox(
width: 800,
height: 25,
child: ColoredBox(color: Colors.yellow),
),
),
],
],
),
);
```Затем мы можем использовать `initExpensiveAndroidView` для принудительной работы PlatformView в режиме HC. При этом в режиме HC появляется множество типичных слоёв native, включая преобразование `FlutterImageView` и её подкласс `PlatformOverlayView`:
Мы можем видеть это с помощью 3D-рендера: красный нативный `BoxPlatformView` отрисован правильно, а затем над ним находятся Flutter-компоненты (часть желтых полос) — они предоставляются через подкласс `FlutterImageView`, называемый `PlatformOverlayView`, который использует отдельную поверхность для рендера:

Затем мы используем метод `initAndroidView` в режиме TLHC и видим, что теперь контейнером является родительская компонента `PlatformViewWrapper`. Эта последняя заменяет Canvas нативного `BoxPlatformView`, чтобы содержимое нативного компонента рисовалось на указанной поверхности:

Используя нативный 3D-рендер, можно заметить, что в данный момент `BoxPlatformView` ничего не рисует на уровне натива, так как его Canvas был заменён на `SurfaceTexture` в памяти:

Что касается `SurfaceTexture`, то здесь стоит отметить, что при создании текстур для THLC и VD используются различные реализации в зависимости от версии Android API. В частности, `SurfaceProducer` имеет особое значение:
До этого момента движок Flutter на платформе Android поддерживал два внешних источника рендера: `SurfaceTexture` (текстура OpenGLES) и `ImageReader` (буфер, готовый к работе GPU). При этом метод `Image.getHardwareBuffer` требует поддержки API уровня 28.Для совместимости с командой Impeller была введена концепция `SurfaceProducer`, которая позволяет Android выбирать "лучшую" поверхность рендера во время выполнения программы. Это применимо не только к сценариям PlatformView, но и к случаям работы с внешними текстурами:
```diff
- TextureRegistry.SurfaceTextureEntry entry = textureRegistry.createSurfaceTexture();
+ TextureRegistry.SurfaceProducer producer = textureRegistry.createSurfaceProducer();
- Surface surface = new Surface(entry.surfaceTexture());
+ Surface surface = producer.getSurface();
Теперь рассмотрим HCPP. Используя метод initHybridAndroidView
, мы активируем режим HCPP, и видим, что структура уровней UI аналогична TLHC, однако родителем служит FlutterMutatorView
из режима HC:
При просмотре 3D-рендера становится очевидно, что нативный компонент BoxPlatformView
может быть полностью отрисован, что указывает на то, что его Canvas не был заменён. Здесь возникает интересный вопрос: какие механизмы позволяют желтым Flutter-компонентам отображаться поверх красного BoxPlatformView? Необходимо обратиться к PlatformViewsController2
, который является временным объектом в контексте HCPP. В его реализации ключевым объектом является SurfaceControl
. При завершении транзакций значение z-координаты устанавливается с помощью метода setLayer
равным 1000:
При рассмотрении изменений можно заметить, что основная логика работы нового PlatformViewsController2
заключается в манипуляциях с объектом SurfaceControl
:
В Android, SurfaceControl
представляет собой класс, используемый для управления и выполнения операций над графическими ресурсами, связанными с системой отображения. Проще говоря, это класс, связанный с управлением Surface
. SurfaceControl
используется для создания и управления Surface
, а также для взаимодействия с SurfaceFlinger
через Transaction
.
В контексте HCPP, Surface
создается с использованием нового объекта SurfaceControl
, а сам объект SurfaceControl
получает свои Transactions
из FlutterView
:
То есть, в режиме HCPP, Flutter использует SurfaceControl.Transaction
для создания нового Surface
, который затем используется SurfaceFlinger
для композиции. Кроме того, с помощью метода setLayer
значение координаты Z Surface
устанавливается на 1000, что позволяет желтому Flutter-контролю отрисовываться поверх красного нативного прямоугольника.
Например, если мы скопируем часть кода, связанную с SurfaceControl
, в простое нативное приложение и установим значение координаты Z Surface
равным 1000, а также нарисуем красный цвет:```java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout rootView = findViewById(R.id.container);
rootView.postDelayed(new Runnable() {
@Override
public void run() {
final SurfaceControl.Builder surfaceControlBuilder = new SurfaceControl.Builder();
surfaceControlBuilder.setBufferSize(500, 500);
surfaceControlBuilder.setFormat(PixelFormat.RGBA_8888);
surfaceControlBuilder.setName("Flutter Overlay Surface");
surfaceControlBuilder.setOpaque(false);
surfaceControlBuilder.setHidden(false);
final SurfaceControl surfaceControl = surfaceControlBuilder.build();
final SurfaceControl.Transaction tx =
binding.container.getRootSurfaceControl().buildReparentTransaction(surfaceControl);
tx.setLayer(surfaceControl, 1000);
tx.apply();
surface1 = new Surface(surfaceControl);
surfaceControl1 = surfaceControl;
}
}, 0);
}
```### Отрисовка содержимого на SurfaceView
```java
// Отрисовываем некоторые данные на SurfaceView
drawOnSurface(surface1, Color.RED);
Затем мы можем рассмотреть конечный результат отрисовки. Мы видим, что фоновый FrameLayout зелёного цвета находится ниже WebView, но созданный поверхность с помощью container.getRootSurfaceControl()
, имеющая ось Z равной 1000, приводит к тому, что красный прямоугольник будет отрисован поверх WebView.
Кроме того, в текущей логике, если движок Engine определяет, что текущий кадр не имеет PlatformView, а предыдущий кадр имел PlatformView, то он вызывает метод hideOverlaySurface2
. Это приводит к вызову platformViewsController2.hideOverlaySurface()
на уровне Java и скрывает ненужные слои:
if (!FrameHasPlatformLayers()) {
frame->Submit();
// Если предыдущий кадр содержал PlatformView, скрываем поверхность.
if (previous_frame_view_count_ > 0) {
jni_facade_->hideOverlaySurface2();
}
jni_facade_->applyTransaction();
return;
}
Таким образом, можно заметить, что HCPP использует SurfaceControl
для создания высокого уровня поверхности (Surface
), чтобы обеспечить смешивание и покрытие при окончательной отрисовке, что аналогично тому, как Compose может использовать SurfaceView
внутри PlatformView, когда SurfaceFlinger
выполняет композицию на основе уровней.
Что касается необходимости использования API 34, это связано с тем, что некоторые API, связанные с SurfaceControl
, требуют более поздних версий. Также стоит отметить, что Android 14 лучше поддерживает низкую задержку рисования через SurfaceControl
, позволяя(Canvas API) ускорять отрисовку на HardwareBuffer:
Если вас интересует логика Engine, вы можете также посмотреть на логику
external_view_embedder_2#SubmitFlutterView
, где используется метод GetLayer
для создания FlutterOverlaySurface
. В настоящее время HCPP находится в бета-версии в главной ветке. В случае успешного внедрения, это приведёт к четырем возможным вариантам реализации для Android PlatformView. В отличие от множества режимов синтеза CALayer на iOS, путь развития Android PlatformView был сложным.Наконец, как вы считаете, станет ли HCPP основой для нового варианта поддержки PlatformView?
PS:
io.flutter.embedding.android.EnableSurfaceControl
используется для управления внутренним использованием Vulkan swapchain или Android SurfaceControl (AHB swapchain) внутри Impeller. В режиме Android SurfaceControl Java-часть создает транзакцию, которая связывается с AHB swapchain.Конечно, реализация AHBSwapchainVK доступна не во всех версиях Android, и при её недоступности система переходит на использование KHR swapchain.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )