Skip to content

Commit 5a6267f

Browse files
committed
RUM-16664: Centralize startup scenario classification in RumStartupScenario.build()
1 parent f288419 commit 5a6267f

4 files changed

Lines changed: 66 additions & 32 deletions

File tree

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ internal interface RumAppStartupDetector {
2020
*/
2121
fun onAppStartupDetected(scenario: RumStartupScenario)
2222
fun onNextActivityCreated(pendingScenario: RumStartupScenario, activity: Activity)
23+
24+
/**
25+
* Called when an Activity is destroyed. Listeners that hold per-activity resources
26+
* (e.g. a [com.datadog.android.rum.startup.RumFirstDrawTimeReporter.Handle]) should
27+
* release them here to avoid memory leaks.
28+
*/
29+
fun onActivityDestroyed(activity: Activity) = Unit
2330
}
2431

2532
fun destroy()

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetectorImpl.kt

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import com.datadog.android.rum.startup.AppStartupActivityPredicate
1616
import java.lang.ref.WeakReference
1717
import java.util.Collections
1818
import java.util.WeakHashMap
19-
import kotlin.time.Duration.Companion.seconds
2019

2120
internal class RumAppStartupDetectorImpl(
2221
private val application: Application,
@@ -54,6 +53,7 @@ internal class RumAppStartupDetectorImpl(
5453
override fun onActivityDestroyed(activity: Activity) {
5554
numberOfActivities--
5655
trackedActivities.remove(activity)
56+
listener.onActivityDestroyed(activity)
5757

5858
if (numberOfActivities == 0) {
5959
isChangingConfigurations = activity.isChangingConfigurations
@@ -102,34 +102,13 @@ internal class RumAppStartupDetectorImpl(
102102

103103
if (isFirstTrackedActivityWithNoPendingStartup) {
104104
val processStartTime = appStartupTime()
105-
106-
val gapNs = now.nanoTime - processStartTime.nanoTime
107-
val hasSavedInstanceStateBundle = savedInstanceState != null
108-
val weakActivity = WeakReference(activity)
109-
110-
val scenario = if (isFirstActivityForProcess) {
111-
if (gapNs > START_GAP_THRESHOLD_NS) {
112-
RumStartupScenario.WarmFirstActivity(
113-
hasSavedInstanceStateBundle = hasSavedInstanceStateBundle,
114-
activity = weakActivity,
115-
appStartActivityOnCreateGapNs = gapNs,
116-
initialTime = now
117-
)
118-
} else {
119-
RumStartupScenario.Cold(
120-
hasSavedInstanceStateBundle = hasSavedInstanceStateBundle,
121-
activity = weakActivity,
122-
appStartActivityOnCreateGapNs = gapNs,
123-
initialTime = processStartTime
124-
)
125-
}
126-
} else {
127-
RumStartupScenario.WarmAfterActivityDestroyed(
128-
hasSavedInstanceStateBundle = hasSavedInstanceStateBundle,
129-
activity = weakActivity,
130-
initialTime = now
131-
)
132-
}
105+
val scenario = RumStartupScenario.build(
106+
isFirstActivityForProcess = isFirstActivityForProcess,
107+
hasSavedInstanceStateBundle = savedInstanceState != null,
108+
activity = WeakReference(activity),
109+
processStartTime = processStartTime,
110+
activityOnCreateTime = now
111+
)
133112

134113
pendingScenario = scenario
135114
listener.onAppStartupDetected(scenario)
@@ -157,7 +136,4 @@ internal class RumAppStartupDetectorImpl(
157136
application.unregisterActivityLifecycleCallbacks(this)
158137
}
159138

160-
companion object {
161-
private val START_GAP_THRESHOLD_NS = 10.seconds.inWholeNanoseconds
162-
}
163139
}

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumStartupScenario.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package com.datadog.android.rum.internal.startup
99
import android.app.Activity
1010
import com.datadog.android.rum.internal.domain.Time
1111
import java.lang.ref.WeakReference
12+
import kotlin.time.Duration.Companion.seconds
1213

1314
internal sealed interface RumStartupScenario {
1415
val initialTime: Time
@@ -34,6 +35,49 @@ internal sealed interface RumStartupScenario {
3435
override val activity: WeakReference<Activity>,
3536
override val initialTime: Time
3637
) : RumStartupScenario
38+
39+
companion object {
40+
internal val START_GAP_THRESHOLD_NS = 10.seconds.inWholeNanoseconds
41+
42+
/**
43+
* Builds the correct [RumStartupScenario] subtype from the raw timing data captured at
44+
* Activity creation time. This is the single source of truth for the Cold / WarmFirstActivity
45+
* / WarmAfterActivityDestroyed classification, shared by [RumAppStartupDetectorImpl] and
46+
* [com.datadog.android.rum.internal.RumFeature].
47+
*/
48+
fun build(
49+
isFirstActivityForProcess: Boolean,
50+
hasSavedInstanceStateBundle: Boolean,
51+
activity: WeakReference<Activity>,
52+
processStartTime: Time,
53+
activityOnCreateTime: Time
54+
): RumStartupScenario {
55+
return if (isFirstActivityForProcess) {
56+
val gapNs = activityOnCreateTime.nanoTime - processStartTime.nanoTime
57+
if (gapNs > START_GAP_THRESHOLD_NS) {
58+
WarmFirstActivity(
59+
hasSavedInstanceStateBundle = hasSavedInstanceStateBundle,
60+
activity = activity,
61+
appStartActivityOnCreateGapNs = gapNs,
62+
initialTime = activityOnCreateTime
63+
)
64+
} else {
65+
Cold(
66+
hasSavedInstanceStateBundle = hasSavedInstanceStateBundle,
67+
activity = activity,
68+
appStartActivityOnCreateGapNs = gapNs,
69+
initialTime = processStartTime
70+
)
71+
}
72+
} else {
73+
WarmAfterActivityDestroyed(
74+
hasSavedInstanceStateBundle = hasSavedInstanceStateBundle,
75+
activity = activity,
76+
initialTime = activityOnCreateTime
77+
)
78+
}
79+
}
80+
}
3781
}
3882

3983
internal val RumStartupScenario.name: String get() = when (this) {

features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetectorImplTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ internal class RumAppStartupDetectorImplTest {
195195
)
196196
)
197197
}
198+
verify(listener).onActivityDestroyed(activity)
198199
verifyNoMoreInteractions(listener)
199200
}
200201

@@ -236,6 +237,7 @@ internal class RumAppStartupDetectorImplTest {
236237
appStartActivityOnCreateGapNs = 3.seconds.inWholeNanoseconds
237238
)
238239
)
240+
verify(listener).onActivityDestroyed(activity)
239241
verifyNoMoreInteractions(listener)
240242
}
241243

@@ -342,6 +344,7 @@ internal class RumAppStartupDetectorImplTest {
342344
)
343345
}
344346

347+
verify(listener).onActivityDestroyed(activity)
345348
verifyNoMoreInteractions(listener)
346349
}
347350

@@ -424,6 +427,8 @@ internal class RumAppStartupDetectorImplTest {
424427
)
425428
}
426429

430+
verify(listener).onActivityDestroyed(activity2)
431+
verify(listener).onActivityDestroyed(activity)
427432
verifyNoMoreInteractions(listener)
428433
}
429434

@@ -478,6 +483,7 @@ internal class RumAppStartupDetectorImplTest {
478483
)
479484
)
480485

486+
verify(listener).onActivityDestroyed(interstitialActivity)
481487
verifyNoMoreInteractions(listener)
482488
}
483489

@@ -670,6 +676,7 @@ internal class RumAppStartupDetectorImplTest {
670676
)
671677
)
672678

679+
verify(listener).onActivityDestroyed(activity1)
673680
verifyNoMoreInteractions(listener)
674681
}
675682

0 commit comments

Comments
 (0)