Skip to content

fix(flutter): Release replay JNI refs#3699

Open
buenaflor wants to merge 10 commits into
mainfrom
fix/android-replay-jni-leaks-clean
Open

fix(flutter): Release replay JNI refs#3699
buenaflor wants to merge 10 commits into
mainfrom
fix/android-replay-jni-leaks-clean

Conversation

@buenaflor
Copy link
Copy Markdown
Contributor

@buenaflor buenaflor commented May 12, 2026

📜 Description

Release JNI references held by the Android replay screenshot path without changing the isolate worker shutdown behavior.

The replay worker now releases the temporary Bitmap$Config.ARGB_8888 reference, the per-capture Bitmap reference, and the isolate-side replay integration handle after recording each screenshot. The native Java owner also releases replay integration handles before replacing or clearing them.

💡 Motivation and Context

Fixes #3633.

The issue reports leaked JNI references in the Android replay path. This keeps the fix scoped to JNI handle ownership instead of adding a generic worker lifecycle hook.

💚 How did you test it?

  • fvm flutter analyze lib/src/native/java/android_replay_recorder.dart
  • fvm flutter analyze lib/src/native/java/android_replay_recorder.dart lib/src/native/java/sentry_native_java.dart lib/src/native/java/sentry_native_java_init.dart
  • fvm flutter test test/isolate/isolate_worker_test.dart
  • Pre-commit hooks ran formatter and flutter analyze --fatal-warnings for sentry_flutter successfully.

📝 Checklist

  • I reviewed submitted code
  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPii is enabled
  • I updated the docs if needed
  • All tests passing
  • No breaking changes

🔮 Next steps

Release Android replay JNI references when worker isolates shut down
and when replay integration handles are replaced. This prevents replay
bitmap/config/native replay references from being retained after use.

Fixes GH-3633
Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


Fixes

  • (flutter) Release replay JNI refs by buenaflor in #3699

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Copy Markdown
Contributor

🚨 Detected changes in high risk code 🚨

High-risk code has higher potential to break the SDK and may be hard to test. To prevent severe bugs, apply the rollout process for releasing such changes and be extra careful when changing and reviewing these files:

  • packages/flutter/lib/src/native/java/android_replay_recorder.dart

@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 55.55556% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.94%. Comparing base (87fcdd8) to head (2ad3c71).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
.../lib/src/telemetry/span/span_capture_pipeline.dart 66.66% 2 Missing ⚠️
packages/flutter/lib/src/sentry_flutter.dart 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3699      +/-   ##
==========================================
- Coverage   86.96%   86.94%   -0.03%     
==========================================
  Files         335      335              
  Lines       11976    11977       +1     
==========================================
- Hits        10415    10413       -2     
- Misses       1561     1564       +3     
Flag Coverage Δ
sentry 86.72% <71.42%> (-0.05%) ⬇️
sentry_dio 97.73% <ø> (ø)
sentry_drift 93.57% <ø> (ø)
sentry_file 65.29% <ø> (ø)
sentry_firebase_remote_config 100.00% <ø> (ø)
sentry_flutter 91.52% <0.00%> (ø)
sentry_hive 77.48% <ø> (ø)
sentry_isar 74.37% <ø> (ø)
sentry_link 21.50% <ø> (ø)
sentry_logging 97.01% <ø> (ø)
sentry_sqflite 88.81% <ø> (ø)
sentry_supabase 97.27% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

Android Performance metrics 🚀

  Plain With Sentry Diff
Startup time 367.07 ms 358.23 ms -8.84 ms
Size 14.31 MiB 15.56 MiB 1.25 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
bfabaf2 408.04 ms 444.38 ms 36.34 ms
fec56a1 370.66 ms 369.28 ms -1.38 ms
8541716 437.14 ms 443.65 ms 6.51 ms
0fb45d0 482.79 ms 554.02 ms 71.23 ms
0fb3800 465.64 ms 536.77 ms 71.13 ms
e04b24b 504.72 ms 516.43 ms 11.71 ms
944b773 470.54 ms 480.18 ms 9.64 ms
cdf371b 367.64 ms 377.02 ms 9.38 ms
0bea8d9 389.45 ms 394.96 ms 5.50 ms
5b9a0da 446.94 ms 449.22 ms 2.28 ms

App size

Revision Plain With Sentry Diff
bfabaf2 13.93 MiB 15.06 MiB 1.13 MiB
fec56a1 14.31 MiB 15.49 MiB 1.19 MiB
8541716 13.93 MiB 15.00 MiB 1.06 MiB
0fb45d0 6.54 MiB 7.70 MiB 1.17 MiB
0fb3800 6.54 MiB 7.69 MiB 1.15 MiB
e04b24b 13.93 MiB 15.00 MiB 1.06 MiB
944b773 13.93 MiB 15.00 MiB 1.06 MiB
cdf371b 13.93 MiB 15.18 MiB 1.25 MiB
0bea8d9 14.31 MiB 15.56 MiB 1.25 MiB
5b9a0da 13.93 MiB 14.93 MiB 1.00 MiB

Previous results on branch: fix/android-replay-jni-leaks-clean

Startup times

Revision Plain With Sentry Diff
62ede5d 387.53 ms 391.07 ms 3.53 ms
99068d4 365.83 ms 363.24 ms -2.59 ms
4a2cb99 398.11 ms 387.70 ms -10.41 ms
3fd6cbd 379.27 ms 364.69 ms -14.58 ms
e3c9090 364.16 ms 353.79 ms -10.37 ms

App size

Revision Plain With Sentry Diff
62ede5d 14.31 MiB 15.56 MiB 1.25 MiB
99068d4 14.31 MiB 15.56 MiB 1.25 MiB
4a2cb99 14.31 MiB 15.56 MiB 1.25 MiB
3fd6cbd 14.31 MiB 15.56 MiB 1.25 MiB
e3c9090 14.31 MiB 15.56 MiB 1.25 MiB

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

iOS Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1250.71 ms 1254.65 ms 3.94 ms
Size 5.73 MiB 6.18 MiB 464.25 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
1adf565 1264.51 ms 1265.22 ms 0.71 ms
d789735 1240.58 ms 1246.41 ms 5.82 ms
2f63d89 1251.67 ms 1263.94 ms 12.27 ms
cdf371b 1246.24 ms 1251.10 ms 4.86 ms
fd88186 1255.06 ms 1252.76 ms -2.30 ms
ce5c42b 1246.06 ms 1251.91 ms 5.85 ms
e5ae2a6 1240.48 ms 1253.26 ms 12.78 ms
944b773 1252.82 ms 1254.08 ms 1.27 ms
73dca78 1246.65 ms 1265.42 ms 18.76 ms
7cfbbd6 1270.63 ms 1285.36 ms 14.72 ms

App size

Revision Plain With Sentry Diff
1adf565 5.66 MiB 6.10 MiB 451.33 KiB
d789735 5.53 MiB 5.96 MiB 443.28 KiB
2f63d89 5.65 MiB 6.09 MiB 446.25 KiB
cdf371b 5.53 MiB 6.02 MiB 501.23 KiB
fd88186 5.53 MiB 6.00 MiB 479.94 KiB
ce5c42b 5.73 MiB 6.17 MiB 455.86 KiB
e5ae2a6 5.65 MiB 6.09 MiB 446.96 KiB
944b773 5.53 MiB 6.00 MiB 479.98 KiB
73dca78 7.86 MiB 9.44 MiB 1.58 MiB
7cfbbd6 7.86 MiB 9.44 MiB 1.58 MiB

Previous results on branch: fix/android-replay-jni-leaks-clean

Startup times

Revision Plain With Sentry Diff
4a2cb99 1241.93 ms 1242.60 ms 0.66 ms
3fd6cbd 1253.15 ms 1252.27 ms -0.88 ms
62ede5d 1247.19 ms 1248.71 ms 1.53 ms
99068d4 1231.61 ms 1241.43 ms 9.82 ms

App size

Revision Plain With Sentry Diff
4a2cb99 5.73 MiB 6.18 MiB 463.51 KiB
3fd6cbd 5.73 MiB 6.18 MiB 463.52 KiB
62ede5d 5.73 MiB 6.18 MiB 463.22 KiB
99068d4 5.73 MiB 6.18 MiB 463.52 KiB

@buenaflor buenaflor marked this pull request as ready for review May 13, 2026 08:57
@buenaflor buenaflor requested a review from denrase as a code owner May 13, 2026 08:57
Copilot AI review requested due to automatic review settings May 13, 2026 08:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses leaked JNI references in the Flutter Android Replay screenshot pipeline by introducing an isolate-side shutdown hook and ensuring replay-related JNI objects are released when replaced or when the worker isolate shuts down.

Changes:

  • Added a WorkerHandler.close() lifecycle hook and invoked it during worker isolate shutdown.
  • Released JNI references in the Android replay screenshot handler (cached Bitmap, ReplayIntegration, and temporary Bitmap$Config.ARGB_8888).
  • Ensured ReplayIntegration handles are released when replaced/cleared in the Java native channel, and added a test validating handler close is called before shutdown.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/flutter/lib/src/isolate/isolate_worker.dart Adds handler shutdown hook and calls it on isolate shutdown.
packages/flutter/lib/src/native/java/android_replay_recorder.dart Releases JNI refs for bitmap/config and replay integration on shutdown.
packages/flutter/lib/src/native/java/sentry_native_java.dart Centralizes replay handle replacement to release previous JNI refs.
packages/flutter/lib/src/native/java/sentry_native_java_init.dart Uses the new replay handle setter and avoids re-reading VERSION_NAME.
packages/flutter/test/isolate/isolate_worker_test.dart Adds coverage ensuring close() is invoked before isolate shutdown.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/flutter/lib/src/isolate/isolate_worker.dart Outdated
buenaflor and others added 3 commits May 13, 2026 11:45
Apply formatter output to touched Dart files so the branch stays clean.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the isolate worker shutdown behavior unchanged and release the replay
integration JNI handle around screenshot recording instead of adding a generic
worker lifecycle callback.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Route Android replay and native Java diagnostics through the shared internal
logger instead of the options logging shim.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread packages/flutter/lib/src/native/java/android_replay_recorder.dart
buenaflor and others added 4 commits May 13, 2026 11:54
Create replay bitmaps per capture and release their JNI references after
recording so the replay worker does not retain bitmap handles.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Release the bitmap config reference in the same cleanup block as the other
per-capture JNI references.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Check the generated nullable bitmap creation result before using it so replay
capture avoids a forced unwrap while preserving cleanup of JNI references.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the replay recorder local cleanup variables in use order.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread packages/flutter/lib/src/native/java/android_replay_recorder.dart Outdated
buenaflor and others added 2 commits May 13, 2026 17:37
Keep the Android replay bitmap cached between same-size captures, but release
it through a replay-specific worker request before the worker shuts down.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Reuse the worker-side replay integration handle during Android replay capture
and release it with the cached bitmap through the replay cleanup request.

Co-Authored-By: GPT-5.5 <noreply@openai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Release leaked JNI references in _AndroidReplayHandler

3 participants