Skip to content

Commit 942f3e6

Browse files
romtsnclaude
andcommitted
fix(screenshot): Avoid crash from uncaught exception in view hierarchy traversal and unnecessary bitmap alloc in MaskRenderer.close()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d97cc91 commit 942f3e6

File tree

2 files changed

+20
-14
lines changed

2 files changed

+20
-14
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,25 @@ private boolean isMaskingEnabled() {
182182
}
183183

184184
private @Nullable ViewHierarchyNode buildViewHierarchy(final @NotNull Activity activity) {
185-
final @Nullable View rootView =
186-
activity.getWindow() != null
187-
&& activity.getWindow().peekDecorView() != null
188-
&& activity.getWindow().peekDecorView().getRootView() != null
189-
? activity.getWindow().peekDecorView().getRootView()
190-
: null;
191-
if (rootView == null) {
185+
try {
186+
final @Nullable View rootView =
187+
activity.getWindow() != null
188+
&& activity.getWindow().peekDecorView() != null
189+
&& activity.getWindow().peekDecorView().getRootView() != null
190+
? activity.getWindow().peekDecorView().getRootView()
191+
: null;
192+
if (rootView == null) {
193+
return null;
194+
}
195+
196+
final ViewHierarchyNode rootNode =
197+
ViewHierarchyNode.Companion.fromView(rootView, null, 0, options.getScreenshot());
198+
ViewsKt.traverse(rootView, rootNode, options.getScreenshot(), options.getLogger());
199+
return rootNode;
200+
} catch (Throwable e) {
201+
options.getLogger().log(SentryLevel.ERROR, "Failed to build view hierarchy", e);
192202
return null;
193203
}
194-
195-
final ViewHierarchyNode rootNode =
196-
ViewHierarchyNode.Companion.fromView(rootView, null, 0, options.getScreenshot());
197-
ViewsKt.traverse(rootView, rootNode, options.getScreenshot(), options.getLogger());
198-
return rootNode;
199204
}
200205

201206
private @Nullable Bitmap applyMasking(

sentry-android-replay/src/main/java/io/sentry/android/replay/util/MaskRenderer.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ internal class MaskRenderer : Closeable {
2626
}
2727

2828
// Single pixel bitmap for dominant color sampling (averaging the region)
29-
internal val singlePixelBitmap: Bitmap by
29+
private val lazySinglePixelBitmap: Lazy<Bitmap> =
3030
lazy(NONE) { Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) }
31+
internal val singlePixelBitmap: Bitmap by lazySinglePixelBitmap
3132
private val singlePixelBitmapCanvas: Canvas by lazy(NONE) { Canvas(singlePixelBitmap) }
3233
private val maskingPaint by lazy(NONE) { Paint() }
3334

@@ -110,7 +111,7 @@ internal class MaskRenderer : Closeable {
110111

111112
/** Releases resources. Call when done with this renderer. */
112113
override fun close() {
113-
if (!singlePixelBitmap.isRecycled) {
114+
if (lazySinglePixelBitmap.isInitialized() && !singlePixelBitmap.isRecycled) {
114115
singlePixelBitmap.recycle()
115116
}
116117
}

0 commit comments

Comments
 (0)