diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index cc17ce762339c..41bd4d95c5f3e 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -91,8 +91,7 @@ void Rasterizer::Teardown() { surface_.reset(); last_layer_tree_.reset(); - if (raster_thread_merger_.get() != nullptr && - raster_thread_merger_.get()->IsMerged()) { + if (raster_thread_merger_ && raster_thread_merger_->IsMerged()) { FML_DCHECK(raster_thread_merger_->IsEnabled()); raster_thread_merger_->UnMergeNowIfLastOne(); raster_thread_merger_->SetMergeUnmergeCallback(nullptr); diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index 08596ba8ad925..e7e9ac18478ec 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -212,7 +212,7 @@ AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context, // |ExternalViewEmbedder| PostPrerollResult AndroidExternalViewEmbedder::PostPrerollAction( fml::RefPtr raster_thread_merger) { - if (!FrameHasPlatformLayers()) { + if (!FrameHasPlatformLayers() || !raster_thread_merger) { return PostPrerollResult::kSuccess; } if (!raster_thread_merger->IsMerged()) { @@ -264,14 +264,12 @@ void AndroidExternalViewEmbedder::BeginFrame( // The surface size changed. Therefore, destroy existing surfaces as // the existing surfaces in the pool can't be recycled. - if (frame_size_ != frame_size && raster_thread_merger->IsOnPlatformThread()) { + if (frame_size_ != frame_size) { surface_pool_->DestroyLayers(jni_facade_); } surface_pool_->SetFrameSize(frame_size); // JNI method must be called on the platform thread. - if (raster_thread_merger->IsOnPlatformThread()) { - jni_facade_->FlutterViewBeginFrame(); - } + jni_facade_->FlutterViewBeginFrame(); frame_size_ = frame_size; device_pixel_ratio_ = device_pixel_ratio; @@ -288,14 +286,12 @@ void AndroidExternalViewEmbedder::EndFrame( fml::RefPtr raster_thread_merger) { surface_pool_->RecycleLayers(); // JNI method must be called on the platform thread. - if (raster_thread_merger->IsOnPlatformThread()) { - jni_facade_->FlutterViewEndFrame(); - } + jni_facade_->FlutterViewEndFrame(); } // |ExternalViewEmbedder| bool AndroidExternalViewEmbedder::SupportsDynamicThreadMerging() { - return true; + return false; } } // namespace flutter diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java b/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java index 843284c99338c..0968bec38aad1 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java @@ -188,6 +188,9 @@ public boolean acquireLatestImage() { if (!isAttachedToFlutterRenderer) { return false; } + if (currentImage != null) { + return true; + } // 1. `acquireLatestImage()` may return null if no new image is available. // 2. There's no guarantee that `onDraw()` is called after `invalidate()`. // For example, the device may not produce new frames if it's in sleep mode diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 3a2d4fcd48f57..ff86d8c86050a 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -11,6 +11,7 @@ import android.graphics.ImageDecoder; import android.graphics.SurfaceTexture; import android.os.Build; +import android.os.Handler; import android.os.Looper; import android.util.Size; import android.view.Surface; @@ -1006,40 +1007,45 @@ private void onPreEngineRestart() { @SuppressWarnings("unused") @UiThread public void onDisplayOverlaySurface(int id, int x, int y, int width, int height) { - ensureRunningOnMainThread(); - if (platformViewsController == null) { - throw new RuntimeException( - "platformViewsController must be set before attempting to position an overlay surface"); - } - platformViewsController.onDisplayOverlaySurface(id, x, y, width, height); + runOnMainThread( + () -> { + if (platformViewsController == null) { + throw new RuntimeException( + "platformViewsController must be set before attempting to position an overlay surface"); + } + platformViewsController.onDisplayOverlaySurface(id, x, y, width, height); + }); } @SuppressWarnings("unused") @UiThread public void onBeginFrame() { - ensureRunningOnMainThread(); - if (platformViewsController == null) { - throw new RuntimeException( - "platformViewsController must be set before attempting to begin the frame"); - } - platformViewsController.onBeginFrame(); + runOnMainThread( + () -> { + if (platformViewsController == null) { + throw new RuntimeException( + "platformViewsController must be set before attempting to begin the frame"); + } + platformViewsController.onBeginFrame(); + }); } @SuppressWarnings("unused") @UiThread public void onEndFrame() { - ensureRunningOnMainThread(); - if (platformViewsController == null) { - throw new RuntimeException( - "platformViewsController must be set before attempting to end the frame"); - } - platformViewsController.onEndFrame(); + runOnMainThread( + () -> { + if (platformViewsController == null) { + throw new RuntimeException( + "platformViewsController must be set before attempting to end the frame"); + } + platformViewsController.onEndFrame(); + }); } @SuppressWarnings("unused") @UiThread public FlutterOverlaySurface createOverlaySurface() { - ensureRunningOnMainThread(); if (platformViewsController == null) { throw new RuntimeException( "platformViewsController must be set before attempting to position an overlay surface"); @@ -1050,12 +1056,14 @@ public FlutterOverlaySurface createOverlaySurface() { @SuppressWarnings("unused") @UiThread public void destroyOverlaySurfaces() { - ensureRunningOnMainThread(); - if (platformViewsController == null) { - throw new RuntimeException( - "platformViewsController must be set before attempting to destroy an overlay surface"); - } - platformViewsController.destroyOverlaySurfaces(); + runOnMainThread( + () -> { + if (platformViewsController == null) { + throw new RuntimeException( + "platformViewsController must be set before attempting to destroy an overlay surface"); + } + platformViewsController.destroyOverlaySurfaces(); + }); } // ----- End Engine Lifecycle Support ---- @@ -1241,13 +1249,15 @@ public void onDisplayPlatformView( int viewWidth, int viewHeight, FlutterMutatorsStack mutatorsStack) { - ensureRunningOnMainThread(); - if (platformViewsController == null) { - throw new RuntimeException( - "platformViewsController must be set before attempting to position a platform view"); - } - platformViewsController.onDisplayPlatformView( - viewId, x, y, width, height, viewWidth, viewHeight, mutatorsStack); + runOnMainThread( + () -> { + if (platformViewsController == null) { + throw new RuntimeException( + "platformViewsController must be set before attempting to position a platform view"); + } + platformViewsController.onDisplayPlatformView( + viewId, x, y, width, height, viewWidth, viewHeight, mutatorsStack); + }); } // TODO(mattcarroll): determine if this is nonull or nullable @@ -1285,6 +1295,15 @@ private void ensureRunningOnMainThread() { } } + private void runOnMainThread(Runnable fn) { + if (Looper.myLooper() == mainLooper) { + fn.run(); + return; + } + final Handler handler = new Handler(mainLooper); + handler.post(fn); + } + /** * Delegate responsible for creating and updating Android-side caches of Flutter's semantics tree * and custom accessibility actions. diff --git a/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java b/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java index d46f5346d7ab3..27413a29f660b 100644 --- a/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java +++ b/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java @@ -120,10 +120,16 @@ public void readyToDisplay( this.mutatorsStack = mutatorsStack; this.left = left; this.top = top; - FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height); - layoutParams.leftMargin = left; - layoutParams.topMargin = top; - setLayoutParams(layoutParams); + final FrameLayout.LayoutParams currentLayout = (FrameLayout.LayoutParams) getLayoutParams(); + if (currentLayout.width != width + || currentLayout.height != height + || currentLayout.leftMargin != left + || currentLayout.topMargin != top) { + final FrameLayout.LayoutParams newLayoutParams = new FrameLayout.LayoutParams(width, height); + newLayoutParams.leftMargin = left; + newLayoutParams.topMargin = top; + setLayoutParams(newLayoutParams); + } setWillNotDraw(false); } diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index c34c539f3f384..87f692fa0f790 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -111,10 +111,23 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega private boolean synchronizeToNativeViewHierarchy = true; // Overlay layer IDs that were displayed since the start of the current frame. - private HashSet currentFrameUsedOverlayLayerIds; + private final HashSet currentFrameUsedOverlayLayerIds; // Platform view IDs that were displayed since the start of the current frame. - private HashSet currentFrameUsedPlatformViewIds; + private final HashSet currentFrameUsedPlatformViewIds; + + // The z-order of the views owned by controller that live in FlutterView. + // This is a stack that provides efficient querying of the views z-order. + private final SparseArray viewZorder; + + // The index of a view in viewZorder. + // + // This is used to determinate if the view is in the expected z-order. + // For example, if viewZorderIndex is 1, the view for index 1 in viewZorder must equal + // to the expected view in order to be considered in the correct order. + // + // This value is reset to 0 when the frame starts. + private int viewZorderIndex = 0; // Used to acquire the original motion events using the motionEventIds. private final MotionEventTracker motionEventTracker; @@ -431,6 +444,7 @@ public PlatformViewsController() { overlayLayerViews = new SparseArray<>(); currentFrameUsedOverlayLayerIds = new HashSet<>(); currentFrameUsedPlatformViewIds = new HashSet<>(); + viewZorder = new SparseArray<>(); platformViews = new SparseArray<>(); platformViewParent = new SparseArray<>(); @@ -502,6 +516,9 @@ public void attachToView(@NonNull View flutterView) { */ public void detachFromView() { destroyOverlaySurfaces(); + destroyHybridPlatformViews(); + viewZorder.clear(); + this.flutterView = null; flutterViewConvertedToImageView = false; @@ -716,6 +733,10 @@ private void flushAllViews() { if (contextToPlatformView.size() > 0) { contextToPlatformView.clear(); } + + destroyOverlaySurfaces(); + destroyHybridPlatformViews(); + viewZorder.clear(); } private void initializeRootImageViewIfNeeded() { @@ -798,15 +819,21 @@ public void onDisplayPlatformView( final FlutterMutatorView parentView = platformViewParent.get(viewId); parentView.readyToDisplay(mutatorsStack, x, y, width, height); + parentView.setVisibility(View.VISIBLE); - parentView.bringToFront(); + bringViewToFrontIfNeeded(parentView); - final FrameLayout.LayoutParams layoutParams = - new FrameLayout.LayoutParams(viewWidth, viewHeight); final View view = platformViews.get(viewId).getView(); if (view != null) { - view.setLayoutParams(layoutParams); - view.bringToFront(); + final ViewGroup.LayoutParams viewLayoutParams = view.getLayoutParams(); + // setLayoutParams schedules a layout even when the params haven't changed. + // This is expensive on low end devices. + if (viewLayoutParams.width != viewWidth || viewLayoutParams.height != viewHeight) { + final FrameLayout.LayoutParams layoutParams = + new FrameLayout.LayoutParams(viewWidth, viewHeight); + + view.setLayoutParams(layoutParams); + } } currentFrameUsedPlatformViewIds.add(viewId); } @@ -832,20 +859,46 @@ public void onDisplayOverlaySurface(int id, int x, int y, int width, int height) ((FlutterView) flutterView).addView(overlayView); } - FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams((int) width, (int) height); - layoutParams.leftMargin = (int) x; - layoutParams.topMargin = (int) y; - overlayView.setLayoutParams(layoutParams); + // setLayoutParams schedules a layout even when the params haven't changed. + // This is expensive on low end devices. + final FrameLayout.LayoutParams currentParams = + (FrameLayout.LayoutParams) overlayView.getLayoutParams(); + if (currentParams.width != width + || currentParams.height != height + || currentParams.leftMargin != x + || currentParams.topMargin != y) { + FrameLayout.LayoutParams layoutParams = + new FrameLayout.LayoutParams((int) width, (int) height); + layoutParams.leftMargin = (int) x; + layoutParams.topMargin = (int) y; + overlayView.setLayoutParams(layoutParams); + } + overlayView.setVisibility(View.VISIBLE); - overlayView.bringToFront(); + bringViewToFrontIfNeeded(overlayView); currentFrameUsedOverlayLayerIds.add(id); } public void onBeginFrame() { + viewZorderIndex = 0; currentFrameUsedOverlayLayerIds.clear(); currentFrameUsedPlatformViewIds.clear(); } + /** @param view The view that should be */ + private void bringViewToFrontIfNeeded(View view) { + if (viewZorder.contains(viewZorderIndex) && viewZorder.get(viewZorderIndex) == view) { + viewZorderIndex++; + return; + } + // Remove the views from the current z-order index to the top of the stack. + viewZorder.removeAtRange(viewZorderIndex, viewZorder.size() - 1); + viewZorder.put(viewZorderIndex, view); + viewZorderIndex++; + io.flutter.Log.d("flutter", "->" + viewZorderIndex + "- view: " + view); + view.bringToFront(); + } + /** * Called by {@code FlutterJNI} when the Flutter frame was submitted. * @@ -966,12 +1019,12 @@ public FlutterOverlaySurface createOverlaySurface() { * *

This method is used only internally by {@code FlutterJNI}. * - *

This member is not intended for public use, and is only visible for testing. + *

This member is not intended for public use. */ public void destroyOverlaySurfaces() { for (int i = 0; i < overlayLayerViews.size(); i++) { - int overlayId = overlayLayerViews.keyAt(i); - FlutterImageView overlayView = overlayLayerViews.valueAt(i); + final int overlayId = overlayLayerViews.keyAt(i); + final FlutterImageView overlayView = overlayLayerViews.valueAt(i); overlayView.detachFromRenderer(); if (flutterView != null) { ((FlutterView) flutterView).removeView(overlayView); @@ -979,4 +1032,17 @@ public void destroyOverlaySurfaces() { } overlayLayerViews.clear(); } + + /** Destroys the platform views that use hybrid composition. */ + @VisibleForTesting + public void destroyHybridPlatformViews() { + for (int i = 0; i < platformViewParent.size(); i++) { + final int overlayId = platformViewParent.keyAt(i); + final FlutterMutatorView view = platformViewParent.valueAt(i); + if (flutterView != null) { + ((FlutterView) flutterView).removeView(view); + } + } + platformViewParent.clear(); + } }