@@ -30,11 +30,13 @@ import io.sentry.android.replay.ExecutorProvider
3030import io.sentry.android.replay.ScreenshotRecorderCallback
3131import io.sentry.android.replay.ScreenshotRecorderConfig
3232import io.sentry.android.replay.util.submitSafely
33+ import io.sentry.util.AutoClosableReentrantLock
3334import io.sentry.util.IntegrationUtils
3435import java.util.WeakHashMap
3536import java.util.concurrent.atomic.AtomicBoolean
3637import java.util.concurrent.atomic.AtomicReference
3738import kotlin.LazyThreadSafetyMode.NONE
39+ import kotlin.use
3840
3941@SuppressLint(" UseKtx" )
4042internal class CanvasStrategy (
@@ -44,7 +46,9 @@ internal class CanvasStrategy(
4446 private val config : ScreenshotRecorderConfig ,
4547) : ScreenshotStrategy {
4648
47- private var screenshot: Bitmap ? = null
49+ @Volatile private var screenshot: Bitmap ? = null
50+
51+ private val screenshotLock = AutoClosableReentrantLock ()
4852 private val prescaledMatrix by
4953 lazy(NONE ) { Matrix ().apply { preScale(config.scaleFactorX, config.scaleFactorY) } }
5054 private val lastCaptureSuccessful = AtomicBoolean (false )
@@ -62,23 +66,21 @@ internal class CanvasStrategy(
6266 if (image.planes.size > 0 ) {
6367 val plane = image.planes[0 ]
6468
65- val buffer = plane.buffer.rewind()
66- val pixelStride = plane.pixelStride
67- val rowStride = plane.rowStride
68- val rowPadding = rowStride - pixelStride * holder.width
69-
70- val bitmap =
71- Bitmap .createBitmap(
72- holder.width + rowPadding / pixelStride,
73- holder.height,
74- Bitmap .Config .ARGB_8888 ,
75- )
76-
77- bitmap.copyPixelsFromBuffer(buffer)
78-
79- screenshot = bitmap
80- lastCaptureSuccessful.set(true )
81- screenshotRecorderCallback?.onScreenshotRecorded(bitmap)
69+ screenshotLock.acquire().use {
70+ if (screenshot == null ) {
71+ screenshot =
72+ Bitmap .createBitmap(holder.width, holder.height, Bitmap .Config .ARGB_8888 )
73+ }
74+ val bitmap = screenshot
75+ if (bitmap == null || bitmap.isRecycled) {
76+ return @use
77+ }
78+
79+ val buffer = plane.buffer.rewind()
80+ bitmap.copyPixelsFromBuffer(buffer)
81+ lastCaptureSuccessful.set(true )
82+ screenshotRecorderCallback?.onScreenshotRecorded(bitmap)
83+ }
8284 }
8385 } finally {
8486 image.close()
@@ -159,9 +161,11 @@ internal class CanvasStrategy(
159161
160162 override fun close () {
161163 isClosed.set(true )
162- screenshot?.apply {
163- if (! isRecycled) {
164- recycle()
164+ screenshotLock.acquire().use {
165+ screenshot?.apply {
166+ if (! isRecycled) {
167+ recycle()
168+ }
165169 }
166170 }
167171 }
@@ -172,10 +176,9 @@ internal class CanvasStrategy(
172176
173177 override fun emitLastScreenshot () {
174178 if (lastCaptureSuccessful()) {
175- screenshot?.let {
176- if (! it.isRecycled) {
177- screenshotRecorderCallback?.onScreenshotRecorded(it)
178- }
179+ val bitmap = screenshot
180+ if (bitmap != null && ! bitmap.isRecycled) {
181+ screenshotRecorderCallback?.onScreenshotRecorded(bitmap)
179182 }
180183 }
181184 }
0 commit comments