Skip to content

Commit 70e9862

Browse files
committed
Re-use bitmap, ensure bitmap creation is thread safe
1 parent b774ddd commit 70e9862

File tree

1 file changed

+28
-25
lines changed
  • sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot

1 file changed

+28
-25
lines changed

sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/CanvasStrategy.kt

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ import io.sentry.android.replay.ExecutorProvider
3030
import io.sentry.android.replay.ScreenshotRecorderCallback
3131
import io.sentry.android.replay.ScreenshotRecorderConfig
3232
import io.sentry.android.replay.util.submitSafely
33+
import io.sentry.util.AutoClosableReentrantLock
3334
import io.sentry.util.IntegrationUtils
3435
import java.util.WeakHashMap
3536
import java.util.concurrent.atomic.AtomicBoolean
3637
import java.util.concurrent.atomic.AtomicReference
3738
import kotlin.LazyThreadSafetyMode.NONE
39+
import kotlin.use
3840

3941
@SuppressLint("UseKtx")
4042
internal 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

Comments
 (0)