Skip to content

[Android] Deprecate FlutterRenderer.getBitmap and add getBitmapAsync - #195929

Draft
mboetger wants to merge 1 commit into
flutter:masterfrom
mboetger:get-bitmap-async
Draft

mboetger wants to merge 1 commit into
flutter:masterfrom
mboetger:get-bitmap-async

Conversation

@mboetger

@mboetger mboetger commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Deprecates the synchronous FlutterRenderer.getBitmap() and FlutterJNI.getBitmap() APIs in the Android embedder, which block the Android UI thread waiting on Rasterizer::Screenshot across the platform/raster threads, and introduces an asynchronous replacement: FlutterRenderer.getBitmapAsync(...).

Summary of Changes

  1. Framework (flutter/screenshot):
    • Adds SystemChannels.screenshot (MethodChannel('flutter/screenshot', StandardMethodCodec())) in packages/flutter/lib/src/services/system_channels.dart.
    • Registers _handleScreenshotMethodCall in RendererBinding.initInstances() (packages/flutter/lib/src/rendering/binding.dart) to handle Screenshot.captureView(int viewId):
      • Locates the RenderView matching viewId and reads its composited OffsetLayer.
      • Rasterizes the layer tree via OffsetLayer.toImage(Offset.zero & targetView.size, pixelRatio: targetView.configuration.devicePixelRatio) and encodes raw RGBA pixels (ui.ImageByteFormat.rawRgba), returning {'width': int, 'height': int, 'bytes': Uint8List} without blocking the Android UI thread.
  2. Android Embedder (ScreenshotChannel & FlutterRenderer):
    • Adds ScreenshotChannel (io.flutter.embedding.engine.systemchannels.ScreenshotChannel) and wires it in FlutterEngine.
    • Adds @Deprecated annotations and migration Javadocs to FlutterRenderer.getBitmap() and FlutterJNI.getBitmap().
    • Adds FlutterRenderer.getBitmapAsync(@NonNull Consumer<Bitmap> callback) and FlutterRenderer.getBitmapAsync(long viewId, @NonNull Consumer<Bitmap> callback).
  3. Tests:
    • Adds framework unit tests in packages/flutter/test/rendering/screenshot_channel_test.dart.
    • Adds Android Robolectric unit tests in ScreenshotChannelTest.java and FlutterRendererTest.java.

Fixes #195720
Related RFC: flutter/rfc#41

Pre-launch Checklist

@mboetger
mboetger requested a review from a team as a code owner November 3, 2026 21:21
@mboetger
mboetger requested review from camsim99 and removed request for a team November 3, 2026 21:21
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Nov 3, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an asynchronous screenshot mechanism via a new ScreenshotChannel (flutter/screenshot), deprecating the existing synchronous getBitmap APIs in FlutterJNI and FlutterRenderer. On the Android side, FlutterRenderer now supports getBitmapAsync to request view captures. On the Dart side, RendererBinding handles these requests by converting the root OffsetLayer to an image and returning its raw RGBA bytes. Feedback on these changes suggests specifying the device pixel ratio in toImage to maintain high-DPI image quality, catching OutOfMemoryError during bitmap creation to prevent crashes, and restricting the visibility of the channel field in ScreenshotChannel to private to improve encapsulation.

return null;
}

final ui.Image image = await rootLayer.toImage(bounds);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation of rootLayer.toImage(bounds) does not specify a pixelRatio, which defaults to 1.0. This will cause screenshots to be captured at logical pixel resolution rather than the device's physical pixel resolution. On high-DPI devices (e.g., with a device pixel ratio of 2.0 or 3.0), this results in a significant loss of image quality and resolution compared to the original synchronous getBitmap() API. Specifying pixelRatio: targetView.flutterView.devicePixelRatio ensures the captured image matches the physical screen resolution.

    final ui.Image image = await rootLayer.toImage(bounds, pixelRatio: targetView.flutterView.devicePixelRatio);

Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(rgbaBytes, 0, (int) expectedBytes));
callback.accept(bitmap);
} catch (RuntimeException e) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Creating a large Bitmap via Bitmap.createBitmap can throw an OutOfMemoryError if the device is low on memory or the screenshot dimensions are extremely large. Since OutOfMemoryError is an Error (not a RuntimeException), it will bypass the current catch (RuntimeException e) block and crash the application. Catching OutOfMemoryError alongside RuntimeException allows the application to fail gracefully and return null to the callback.

Suggested change
} catch (RuntimeException e) {
} catch (OutOfMemoryError | RuntimeException e) {

// Four bytes per pixel for premultiplied RGBA_8888 / ARGB_8888 buffers.
private static final int BYTES_PER_PIXEL_RGBA_8888 = 4;

@NonNull public final MethodChannel channel;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The channel field is not accessed outside of the ScreenshotChannel class. To improve encapsulation and adhere to standard Java best practices, it should be declared as private instead of public.

Suggested change
@NonNull public final MethodChannel channel;
@NonNull private final MethodChannel channel;
References
  1. Fields should be declared with the most restrictive access level possible to enforce encapsulation.

@mboetger
mboetger marked this pull request as draft November 3, 2026 21:27
@flutter-dashboard

Copy link
Copy Markdown

This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@github-actions github-actions Bot added platform-android Android applications specifically framework flutter/packages/flutter repository. See also f: labels. engine flutter/engine related. See also e: labels. team-android Owned by Android platform team labels Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD engine flutter/engine related. See also e: labels. framework flutter/packages/flutter repository. See also f: labels. platform-android Android applications specifically team-android Owned by Android platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deprecate Android FlutterRenderer.getBitmap

1 participant