@@ -21,8 +21,8 @@ import io.sentry.SentryLevel.WARNING
2121import io.sentry.SentryOptions
2222import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode
2323import java.lang.ref.WeakReference
24- import java.util.WeakHashMap
2524import java.util.concurrent.atomic.AtomicBoolean
25+ import java.util.concurrent.atomic.AtomicReference
2626import kotlin.system.measureTimeMillis
2727
2828@TargetApi(26 )
@@ -35,7 +35,7 @@ internal class ScreenshotRecorder(
3535 private var rootView: WeakReference <View >? = null
3636 private val thread = HandlerThread (" SentryReplayRecorder" ).also { it.start() }
3737 private val handler = Handler (thread.looper)
38- private val bitmapToVH = WeakHashMap < Bitmap , ViewHierarchyNode >()
38+ private val pendingViewHierarchy = AtomicReference < ViewHierarchyNode >()
3939 private val maskingPaint = Paint ()
4040 private val singlePixelBitmap: Bitmap = Bitmap .createBitmap(
4141 1 ,
@@ -51,6 +51,8 @@ internal class ScreenshotRecorder(
5151 private var lastScreenshot: Bitmap ? = null
5252
5353 fun capture () {
54+ val viewHierarchy = pendingViewHierarchy.get()
55+
5456 if (! isCapturing.get()) {
5557 options.logger.log(DEBUG , " ScreenshotRecorder is paused, not capturing screenshot" )
5658 return
@@ -87,13 +89,6 @@ internal class ScreenshotRecorder(
8789
8890 // postAtFrontOfQueue to ensure the view hierarchy and bitmap are ase close in-sync as possible
8991 Handler (Looper .getMainLooper()).postAtFrontOfQueue {
90- val time = measureTimeMillis {
91- val rootNode = ViewHierarchyNode .fromView(root)
92- root.traverse(rootNode)
93- bitmapToVH[bitmap] = rootNode
94- }
95- options.logger.log(DEBUG , " Took %d ms to capture view hierarchy" , time)
96-
9792 try {
9893 PixelCopy .request(
9994 window,
@@ -102,17 +97,14 @@ internal class ScreenshotRecorder(
10297 if (copyResult != PixelCopy .SUCCESS ) {
10398 options.logger.log(INFO , " Failed to capture replay recording: %d" , copyResult)
10499 bitmap.recycle()
105- bitmapToVH.remove(bitmap)
106100 return @request
107101 }
108102
109- val viewHierarchy = bitmapToVH[bitmap]
110103 val scaledBitmap: Bitmap
111104
112105 if (viewHierarchy == null ) {
113106 options.logger.log(INFO , " Failed to determine view hierarchy, not capturing" )
114107 bitmap.recycle()
115- bitmapToVH.remove(bitmap)
116108 return @request
117109 } else {
118110 scaledBitmap = Bitmap .createScaledBitmap(
@@ -149,19 +141,30 @@ internal class ScreenshotRecorder(
149141
150142 scaledBitmap.recycle()
151143 bitmap.recycle()
152- bitmapToVH.remove(bitmap)
153144 },
154145 handler
155146 )
156147 } catch (e: Throwable ) {
157148 options.logger.log(WARNING , " Failed to capture replay recording" , e)
158149 bitmap.recycle()
159- bitmapToVH.remove(bitmap)
160150 }
161151 }
162152 }
163153
164154 override fun onDraw () {
155+ val root = rootView?.get()
156+ if (root == null || root.width <= 0 || root.height <= 0 || ! root.isShown) {
157+ options.logger.log(DEBUG , " Root view is invalid, not capturing screenshot" )
158+ return
159+ }
160+
161+ val time = measureTimeMillis {
162+ val rootNode = ViewHierarchyNode .fromView(root)
163+ root.traverse(rootNode)
164+ pendingViewHierarchy.set(rootNode)
165+ }
166+ options.logger.log(DEBUG , " Took %d ms to capture view hierarchy" , time)
167+
165168 contentChanged.set(true )
166169 }
167170
@@ -194,7 +197,7 @@ internal class ScreenshotRecorder(
194197 unbind(rootView?.get())
195198 rootView?.clear()
196199 lastScreenshot?.recycle()
197- bitmapToVH.clear( )
200+ pendingViewHierarchy.set( null )
198201 isCapturing.set(false )
199202 thread.quitSafely()
200203 }
0 commit comments