Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f3c466d

Browse files
committed
Workaround HardwareRenderer breakage in Android 14
- Destroy ImageReaders on memory trim. - Unset the VirtualDisplay's surface on memory trim. - On resume, recreate ImageReaders. - On resume, do a dumb little dance and then set the VirtualDisplay's surface Fixes: flutter/flutter#146499 Fixes: flutter/flutter#144219 Internal bug: b/335646931 Android Fix: https://googleplex-android-review.git.corp.google.com/c/platform/frameworks/base/+/27015418 Unclear when the Android fix will be available in phones.
1 parent c093562 commit f3c466d

File tree

4 files changed

+73
-7
lines changed

4 files changed

+73
-7
lines changed

shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,6 @@ public boolean onPreDraw() {
560560
* lifecycle.
561561
*/
562562
void onResume() {
563-
Log.v(TAG, "onResume()");
564563
ensureAlive();
565564
if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
566565
flutterEngine.getLifecycleChannel().appIsResumed();
@@ -579,10 +578,10 @@ void onResume() {
579578
// TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if
580579
// possible.
581580
void onPostResume() {
582-
Log.v(TAG, "onPostResume()");
583581
ensureAlive();
584582
if (flutterEngine != null) {
585583
updateSystemUiOverlays();
584+
flutterEngine.getPlatformViewsController().onResume();
586585
} else {
587586
Log.w(TAG, "onPostResume() invoked before FlutterFragment was attached to an Activity.");
588587
}
@@ -1020,6 +1019,7 @@ void onTrimMemory(int level) {
10201019
flutterEngine.getSystemChannel().sendMemoryPressureWarning();
10211020
}
10221021
flutterEngine.getRenderer().onTrimMemory(level);
1022+
flutterEngine.getPlatformViewsController().onTrimMemory(level);
10231023
}
10241024
}
10251025

shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static io.flutter.Build.API_LEVELS;
88

99
import android.annotation.TargetApi;
10+
import android.content.ComponentCallbacks2;
1011
import android.graphics.Bitmap;
1112
import android.graphics.ImageFormat;
1213
import android.graphics.Rect;
@@ -418,17 +419,18 @@ final class ImageReaderSurfaceProducer
418419
// Flip when debugging to see verbose logs.
419420
private static final boolean VERBOSE_LOGS = false;
420421

421-
// If we cleanup the ImageReaders on memory pressure it breaks VirtualDisplay
422-
// backed platform views. Disable for now as this is only necessary to work
423-
// around a Samsung-specific Android 14 bug.
424-
private static final boolean CLEANUP_ON_MEMORY_PRESSURE = false;
422+
// We must always cleanup on memory pressure on Android 14 due to a bug in Android.
423+
// It is safe to do on all versions so we unconditionally have this set to true.
424+
private static final boolean CLEANUP_ON_MEMORY_PRESSURE = true;
425425

426426
private final long id;
427427

428428
private boolean released;
429429
// Will be true in tests and on Android API < 33.
430430
private boolean ignoringFence = false;
431431

432+
private boolean trimOnMemoryPressure = CLEANUP_ON_MEMORY_PRESSURE;
433+
432434
// The requested width and height are updated by setSize.
433435
private int requestedWidth = 1;
434436
private int requestedHeight = 1;
@@ -659,7 +661,10 @@ PerImage dequeueImage() {
659661

660662
@Override
661663
public void onTrimMemory(int level) {
662-
if (!CLEANUP_ON_MEMORY_PRESSURE) {
664+
if (!trimOnMemoryPressure) {
665+
return;
666+
}
667+
if (level < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
663668
return;
664669
}
665670
cleanup();

shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static io.flutter.Build.API_LEVELS;
1010

1111
import android.annotation.TargetApi;
12+
import android.content.ComponentCallbacks2;
1213
import android.content.Context;
1314
import android.content.MutableContextWrapper;
1415
import android.os.Build;
@@ -1053,6 +1054,23 @@ private void diposeAllViews() {
10531054
}
10541055
}
10551056

1057+
public void onTrimMemory(int level) {
1058+
if (level < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
1059+
return;
1060+
}
1061+
for (VirtualDisplayController vdc : vdControllers.values()) {
1062+
vdc.clearSurface();
1063+
}
1064+
}
1065+
1066+
// Called after the application has been resumed. Should undo anything done
1067+
// in onTrimMemory above.
1068+
public void onResume() {
1069+
for (VirtualDisplayController vdc : vdControllers.values()) {
1070+
vdc.resetSurface();
1071+
}
1072+
}
1073+
10561074
/**
10571075
* Disposes a single
10581076
*

shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,49 @@ public void dispatchTouchEvent(MotionEvent event) {
284284
presentation.dispatchTouchEvent(event);
285285
}
286286

287+
public void clearSurface() {
288+
virtualDisplay.setSurface(null);
289+
}
290+
291+
public void resetSurface() {
292+
final int width = getRenderTargetWidth();
293+
final int height = getRenderTargetHeight();
294+
final boolean isFocused = getView().isFocused();
295+
final SingleViewPresentation.PresentationState presentationState = presentation.detachState();
296+
297+
// We detach the surface to prevent it being destroyed when releasing the vd.
298+
virtualDisplay.setSurface(null);
299+
virtualDisplay.release();
300+
final DisplayManager displayManager =
301+
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
302+
int flags = 0;
303+
virtualDisplay =
304+
displayManager.createVirtualDisplay(
305+
"flutter-vd#" + viewId,
306+
width,
307+
height,
308+
densityDpi,
309+
renderTarget.getSurface(),
310+
flags,
311+
callback,
312+
null /* handler */);
313+
// Create a new SingleViewPresentation and show() it before we cancel() the existing
314+
// presentation. Calling show() and cancel() in this order fixes
315+
// https://github.com/flutter/flutter/issues/26345 and maintains seamless transition
316+
// of the contents of the presentation.
317+
SingleViewPresentation newPresentation =
318+
new SingleViewPresentation(
319+
context,
320+
virtualDisplay.getDisplay(),
321+
accessibilityEventsDelegate,
322+
presentationState,
323+
focusChangeListener,
324+
isFocused);
325+
newPresentation.show();
326+
presentation.cancel();
327+
presentation = newPresentation;
328+
}
329+
287330
static class OneTimeOnDrawListener implements ViewTreeObserver.OnDrawListener {
288331
static void schedule(View view, Runnable runnable) {
289332
OneTimeOnDrawListener listener = new OneTimeOnDrawListener(view, runnable);

0 commit comments

Comments
 (0)