Skip to content

Commit ee6ab95

Browse files
authored
[QA] Make replay lazy and faster (#3799)
* Make replay lazy and faster * Changelog
1 parent bd82483 commit ee6ab95

File tree

6 files changed

+35
-31
lines changed

6 files changed

+35
-31
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- Fix potential ANRs due to NDK System.loadLibrary calls ([#3670](https://github.com/getsentry/sentry-java/pull/3670))
1616
- Fix slow `Log` calls on app startup ([#3793](https://github.com/getsentry/sentry-java/pull/3793))
1717
- Fix slow Integration name parsing ([#3794](https://github.com/getsentry/sentry-java/pull/3794))
18+
- Session Replay: Reduce startup and capture overhead ([#3799](https://github.com/getsentry/sentry-java/pull/3799))
1819

1920
## 7.15.0
2021

sentry-android-replay/src/main/java/io/sentry/android/replay/DefaultReplayBreadcrumbConverter.kt

+10-8
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ import kotlin.LazyThreadSafetyMode.NONE
1212
public open class DefaultReplayBreadcrumbConverter : ReplayBreadcrumbConverter {
1313
internal companion object {
1414
private val snakecasePattern by lazy(NONE) { "_[a-z]".toRegex() }
15-
private val supportedNetworkData = setOf(
16-
"status_code",
17-
"method",
18-
"response_content_length",
19-
"request_content_length",
20-
"http.response_content_length",
21-
"http.request_content_length"
22-
)
15+
private val supportedNetworkData by lazy(NONE) {
16+
setOf(
17+
"status_code",
18+
"method",
19+
"response_content_length",
20+
"request_content_length",
21+
"http.response_content_length",
22+
"http.request_content_length"
23+
)
24+
}
2325
}
2426

2527
private var lastConnectivityState: String? = null

sentry-android-replay/src/main/java/io/sentry/android/replay/ScreenshotRecorder.kt

+14-9
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import java.util.concurrent.Executors
3636
import java.util.concurrent.ThreadFactory
3737
import java.util.concurrent.atomic.AtomicBoolean
3838
import java.util.concurrent.atomic.AtomicReference
39+
import kotlin.LazyThreadSafetyMode.NONE
3940
import kotlin.math.roundToInt
4041

4142
@TargetApi(26)
@@ -51,15 +52,19 @@ internal class ScreenshotRecorder(
5152
}
5253
private var rootView: WeakReference<View>? = null
5354
private val pendingViewHierarchy = AtomicReference<ViewHierarchyNode>()
54-
private val maskingPaint = Paint()
55-
private val singlePixelBitmap: Bitmap = Bitmap.createBitmap(
56-
1,
57-
1,
58-
Bitmap.Config.ARGB_8888
59-
)
60-
private val singlePixelBitmapCanvas: Canvas = Canvas(singlePixelBitmap)
61-
private val prescaledMatrix = Matrix().apply {
62-
preScale(config.scaleFactorX, config.scaleFactorY)
55+
private val maskingPaint by lazy(NONE) { Paint() }
56+
private val singlePixelBitmap: Bitmap by lazy(NONE) {
57+
Bitmap.createBitmap(
58+
1,
59+
1,
60+
Bitmap.Config.ARGB_8888
61+
)
62+
}
63+
private val singlePixelBitmapCanvas: Canvas by lazy(NONE) { Canvas(singlePixelBitmap) }
64+
private val prescaledMatrix by lazy(NONE) {
65+
Matrix().apply {
66+
preScale(config.scaleFactorX, config.scaleFactorY)
67+
}
6368
}
6469
private val contentChanged = AtomicBoolean(false)
6570
private val isCapturing = AtomicBoolean(true)

sentry-android-replay/src/main/java/io/sentry/android/replay/capture/BaseCaptureStrategy.kt

-4
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,6 @@ internal abstract class BaseCaptureStrategy(
209209
}
210210
}
211211

212-
init {
213-
runInBackground { onChange(propertyName, initialValue, initialValue) }
214-
}
215-
216212
override fun getValue(thisRef: Any?, property: KProperty<*>): T? = value.get()
217213

218214
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {

sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt

+8-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import androidx.compose.ui.graphics.isUnspecified
88
import androidx.compose.ui.graphics.toArgb
99
import androidx.compose.ui.layout.LayoutCoordinates
1010
import androidx.compose.ui.layout.findRootCoordinates
11-
import androidx.compose.ui.layout.positionInWindow
1211
import androidx.compose.ui.node.LayoutNode
1312
import androidx.compose.ui.node.Owner
1413
import androidx.compose.ui.semantics.SemanticsActions
@@ -87,12 +86,13 @@ internal object ComposeViewHierarchyNode {
8786
(semantics == null || !semantics.contains(SemanticsProperties.InvisibleToUser)) &&
8887
visibleRect.height() > 0 && visibleRect.width() > 0
8988
val isEditable = semantics?.contains(SemanticsActions.SetText) == true
90-
val positionInWindow = node.coordinates.positionInWindow()
9189
return when {
9290
semantics?.contains(SemanticsProperties.Text) == true || isEditable -> {
9391
val shouldMask = isVisible && node.shouldMask(isImage = false, options)
9492

9593
parent?.setImportantForCaptureToAncestors(true)
94+
// TODO: if we get reports that it's slow, we can drop this, and just mask
95+
// TODO: the whole view instead of per-line
9696
val textLayoutResults = mutableListOf<TextLayoutResult>()
9797
semantics?.getOrNull(SemanticsActions.GetTextLayoutResult)
9898
?.action
@@ -108,8 +108,8 @@ internal object ComposeViewHierarchyNode {
108108
TextViewHierarchyNode(
109109
layout = if (textLayoutResults.isNotEmpty() && !isEditable) ComposeTextLayout(textLayoutResults.first(), hasFillModifier) else null,
110110
dominantColor = textColor?.toArgb()?.toOpaque(),
111-
x = positionInWindow.x,
112-
y = positionInWindow.y,
111+
x = visibleRect.left.toFloat(),
112+
y = visibleRect.top.toFloat(),
113113
width = node.width,
114114
height = node.height,
115115
elevation = (parent?.elevation ?: 0f),
@@ -128,8 +128,8 @@ internal object ComposeViewHierarchyNode {
128128

129129
parent?.setImportantForCaptureToAncestors(true)
130130
ImageViewHierarchyNode(
131-
x = positionInWindow.x,
132-
y = positionInWindow.y,
131+
x = visibleRect.left.toFloat(),
132+
y = visibleRect.top.toFloat(),
133133
width = node.width,
134134
height = node.height,
135135
elevation = (parent?.elevation ?: 0f),
@@ -147,8 +147,8 @@ internal object ComposeViewHierarchyNode {
147147
// TODO: traverse the ViewHierarchyNode here again. For now we can recommend
148148
// TODO: using custom modifiers to obscure the entire node if it's sensitive
149149
GenericViewHierarchyNode(
150-
x = positionInWindow.x,
151-
y = positionInWindow.y,
150+
x = visibleRect.left.toFloat(),
151+
y = visibleRect.top.toFloat(),
152152
width = node.width,
153153
height = node.height,
154154
elevation = (parent?.elevation ?: 0f),

sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ViewHierarchyNode.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ sealed class ViewHierarchyNode(
239239
private fun Class<*>.isAssignableFrom(set: Set<String>): Boolean {
240240
var cls: Class<*>? = this
241241
while (cls != null) {
242-
val canonicalName = cls.canonicalName
243-
if (canonicalName != null && set.contains(canonicalName)) {
242+
val canonicalName = cls.name
243+
if (set.contains(canonicalName)) {
244244
return true
245245
}
246246
cls = cls.superclass

0 commit comments

Comments
 (0)