@@ -2,6 +2,8 @@ package io.sentry.android.replay
22
33import android.annotation.TargetApi
44import android.graphics.Point
5+ import android.os.Handler
6+ import android.os.HandlerThread
57import android.view.View
68import android.view.ViewTreeObserver
79import io.sentry.SentryLevel.DEBUG
@@ -24,15 +26,20 @@ internal class WindowRecorder(
2426 private val windowCallback : WindowCallback ,
2527 private val mainLooperHandler : MainLooperHandler ,
2628 private val replayExecutor : ScheduledExecutorService ,
27- ) : Recorder, OnRootViewsChangedListener {
29+ ) : Recorder, OnRootViewsChangedListener, ExecutorProvider {
2830
2931 private val isRecording = AtomicBoolean (false )
3032 private val rootViews = ArrayList <WeakReference <View >>()
3133 private var lastKnownWindowSize: Point = Point ()
3234 private val rootViewsLock = AutoClosableReentrantLock ()
3335 private val capturerLock = AutoClosableReentrantLock ()
36+ private val backgroundProcessingHandlerLock = AutoClosableReentrantLock ()
37+
3438 @Volatile private var capturer: Capturer ? = null
3539
40+ @Volatile private var backgroundProcessingHandlerThread: HandlerThread ? = null
41+ @Volatile private var backgroundProcessingHandler: Handler ? = null
42+
3643 private class Capturer (
3744 private val options : SentryOptions ,
3845 private val mainLooperHandler : MainLooperHandler ,
@@ -174,14 +181,7 @@ internal class WindowRecorder(
174181 }
175182
176183 capturer?.config = config
177- capturer?.recorder =
178- ScreenshotRecorder (
179- config,
180- options,
181- mainLooperHandler,
182- replayExecutor,
183- screenshotRecorderCallback,
184- )
184+ capturer?.recorder = ScreenshotRecorder (config, options, this , screenshotRecorderCallback)
185185
186186 val newRoot = rootViews.lastOrNull()?.get()
187187 if (newRoot != null ) {
@@ -229,6 +229,40 @@ internal class WindowRecorder(
229229 override fun close () {
230230 reset()
231231 mainLooperHandler.removeCallbacks(capturer)
232+ backgroundProcessingHandlerLock.acquire().use {
233+ backgroundProcessingHandler?.removeCallbacksAndMessages(null )
234+ backgroundProcessingHandlerThread?.quitSafely()
235+ }
232236 stop()
233237 }
238+
239+ override fun getExecutor (): ScheduledExecutorService = replayExecutor
240+
241+ override fun getMainLooperHandler (): MainLooperHandler = mainLooperHandler
242+
243+ override fun getBackgroundHandler (): Handler {
244+ // only start the background thread if it's actually needed, as it's only used by Canvas Capture
245+ // Strategy
246+ if (backgroundProcessingHandler == null ) {
247+ backgroundProcessingHandlerLock.acquire().use {
248+ if (backgroundProcessingHandler == null ) {
249+ backgroundProcessingHandlerThread = HandlerThread (" SentryReplayBackgroundProcessing" )
250+ backgroundProcessingHandlerThread?.start()
251+ backgroundProcessingHandler = Handler (backgroundProcessingHandlerThread!! .looper)
252+ }
253+ }
254+ }
255+ return backgroundProcessingHandler!!
256+ }
257+ }
258+
259+ internal interface ExecutorProvider {
260+ /* * Returns an executor suitable for background tasks. */
261+ fun getExecutor (): ScheduledExecutorService
262+
263+ /* * Returns a handler associated with the main thread looper. */
264+ fun getMainLooperHandler (): MainLooperHandler
265+
266+ /* * Returns a handler associated with a background thread looper. */
267+ fun getBackgroundHandler (): Handler
234268}
0 commit comments