@@ -24,18 +24,17 @@ import androidx.metrics.performance.FrameData
24
24
import androidx.metrics.performance.JankStats
25
25
import com.datadog.android.api.InternalLogger
26
26
import com.datadog.android.core.internal.system.BuildSdkVersionProvider
27
+ import com.datadog.android.rum.internal.domain.FrameMetricsData
27
28
import java.lang.ref.WeakReference
28
29
import java.util.WeakHashMap
29
- import java.util.concurrent.TimeUnit
30
30
31
31
/* *
32
32
* Utility class listening to frame rate information.
33
33
*/
34
34
internal class JankStatsActivityLifecycleListener (
35
- private val vitalObserver : VitalObserver ,
35
+ private val delegates : List < FrameStateListener > ,
36
36
private val internalLogger : InternalLogger ,
37
37
private val jankStatsProvider : JankStatsProvider = JankStatsProvider .DEFAULT ,
38
- private var screenRefreshRate : Double = 60.0 ,
39
38
private var buildSdkVersionProvider : BuildSdkVersionProvider = BuildSdkVersionProvider .DEFAULT
40
39
) : ActivityLifecycleCallbacks, JankStats.OnFrameListener {
41
40
@@ -44,7 +43,8 @@ internal class JankStatsActivityLifecycleListener(
44
43
internal val activeActivities = WeakHashMap <Window , MutableList <WeakReference <Activity >>>()
45
44
internal var display: Display ? = null
46
45
private var frameMetricsListener: DDFrameMetricsListener ? = null
47
- internal var frameDeadline = SIXTEEN_MS_NS
46
+
47
+ private val frameMetricsData = FrameMetricsData ()
48
48
49
49
// region ActivityLifecycleCallbacks
50
50
@MainThread
@@ -154,24 +154,7 @@ internal class JankStatsActivityLifecycleListener(
154
154
// region JankStats.OnFrameListener
155
155
156
156
override fun onFrame (volatileFrameData : FrameData ) {
157
- val durationNs = volatileFrameData.frameDurationUiNanos
158
- if (durationNs > 0.0 ) {
159
- var frameRate = (ONE_SECOND_NS / durationNs)
160
-
161
- if (buildSdkVersionProvider.version >= Build .VERSION_CODES .S ) {
162
- screenRefreshRate = ONE_SECOND_NS / frameDeadline
163
- } else if (buildSdkVersionProvider.version == Build .VERSION_CODES .R ) {
164
- screenRefreshRate = display?.refreshRate?.toDouble() ? : SIXTY_FPS
165
- }
166
-
167
- // If normalized frame rate is still at over 60fps it means the frame rendered
168
- // quickly enough for the devices refresh rate.
169
- frameRate = (frameRate * (SIXTY_FPS / screenRefreshRate)).coerceAtMost(MAX_FPS )
170
-
171
- if (frameRate > MIN_FPS ) {
172
- vitalObserver.onNewSample(frameRate)
173
- }
174
- }
157
+ delegates.forEach { it.onFrame(volatileFrameData) }
175
158
}
176
159
177
160
// endregion
@@ -233,30 +216,22 @@ internal class JankStatsActivityLifecycleListener(
233
216
}
234
217
val handler = Handler (Looper .getMainLooper())
235
218
// Only hardware accelerated views can be tracked with metrics listener
236
- if (window.peekDecorView()?.isHardwareAccelerated == true ) {
237
- frameMetricsListener?.let { listener ->
238
- try {
239
- @Suppress(" UnsafeThirdPartyFunctionCall" ) // Listener can't be null here
240
- window.addOnFrameMetricsAvailableListener(listener, handler)
241
- } catch (e: IllegalStateException ) {
242
- internalLogger.log(
243
- InternalLogger .Level .ERROR ,
244
- InternalLogger .Target .MAINTAINER ,
245
- { " Unable to attach JankStatsListener to window" },
246
- e
247
- )
248
- }
219
+ frameMetricsListener?.let { listener ->
220
+ try {
221
+ @Suppress(" UnsafeThirdPartyFunctionCall" ) // Listener can't be null here
222
+ window.addOnFrameMetricsAvailableListener(listener, handler)
223
+ } catch (e: IllegalStateException ) {
224
+ internalLogger.log(
225
+ InternalLogger .Level .ERROR ,
226
+ InternalLogger .Target .MAINTAINER ,
227
+ { " Unable to attach JankStatsListener to window" },
228
+ e
229
+ )
249
230
}
250
- } else {
251
- internalLogger.log(
252
- InternalLogger .Level .WARN ,
253
- InternalLogger .Target .MAINTAINER ,
254
- { " Unable to attach JankStatsListener to window, decorView is null or not hardware accelerated" }
255
- )
256
231
}
257
232
}
258
233
259
- @RequiresApi(Build .VERSION_CODES .N )
234
+ @RequiresApi(Build .VERSION_CODES .S )
260
235
private fun unregisterMetricListener (window : Window ) {
261
236
try {
262
237
window.removeOnFrameMetricsAvailableListener(frameMetricsListener)
@@ -279,7 +254,34 @@ internal class JankStatsActivityLifecycleListener(
279
254
frameMetrics : FrameMetrics ,
280
255
dropCountSinceLastInvocation : Int
281
256
) {
282
- frameDeadline = frameMetrics.getMetric(FrameMetrics .DEADLINE )
257
+ delegates.forEach { it.onFrameMetricsData(frameMetricsData.update(frameMetrics)) }
258
+ }
259
+ }
260
+
261
+ @RequiresApi(Build .VERSION_CODES .N )
262
+ private fun FrameMetricsData.update (frameMetrics : FrameMetrics ) = apply {
263
+ displayRefreshRate = display?.refreshRate?.toDouble() ? : SIXTY_FPS
264
+ if (buildSdkVersionProvider.version >= Build .VERSION_CODES .N ) {
265
+ unknownDelayDuration = frameMetrics.getMetric(FrameMetrics .UNKNOWN_DELAY_DURATION )
266
+ inputHandlingDuration = frameMetrics.getMetric(FrameMetrics .INPUT_HANDLING_DURATION )
267
+ animationDuration = frameMetrics.getMetric(FrameMetrics .ANIMATION_DURATION )
268
+ layoutMeasureDuration = frameMetrics.getMetric(FrameMetrics .LAYOUT_MEASURE_DURATION )
269
+ drawDuration = frameMetrics.getMetric(FrameMetrics .DRAW_DURATION )
270
+ syncDuration = frameMetrics.getMetric(FrameMetrics .SYNC_DURATION )
271
+ commandIssueDuration = frameMetrics.getMetric(FrameMetrics .COMMAND_ISSUE_DURATION )
272
+ swapBuffersDuration = frameMetrics.getMetric(FrameMetrics .SWAP_BUFFERS_DURATION )
273
+ totalDuration = frameMetrics.getMetric(FrameMetrics .TOTAL_DURATION )
274
+ firstDrawFrame = frameMetrics.getMetric(FrameMetrics .FIRST_DRAW_FRAME ) == 1L
275
+ }
276
+ @SuppressLint(" InlinedApi" )
277
+ if (buildSdkVersionProvider.version >= Build .VERSION_CODES .O ) {
278
+ intendedVsyncTimestamp = frameMetrics.getMetric(FrameMetrics .INTENDED_VSYNC_TIMESTAMP )
279
+ vsyncTimestamp = frameMetrics.getMetric(FrameMetrics .VSYNC_TIMESTAMP )
280
+ }
281
+ @SuppressLint(" InlinedApi" )
282
+ if (buildSdkVersionProvider.version >= Build .VERSION_CODES .S ) {
283
+ gpuDuration = frameMetrics.getMetric(FrameMetrics .GPU_DURATION )
284
+ deadline = frameMetrics.getMetric(FrameMetrics .DEADLINE )
283
285
}
284
286
}
285
287
@@ -292,12 +294,6 @@ internal class JankStatsActivityLifecycleListener(
292
294
" shouldn't happen."
293
295
internal const val JANK_STATS_TRACKING_DISABLE_ERROR =
294
296
" Failed to disable JankStats tracking"
295
-
296
- private val ONE_SECOND_NS : Double = TimeUnit .SECONDS .toNanos(1 ).toDouble()
297
-
298
- private const val MIN_FPS : Double = 1.0
299
- private const val MAX_FPS : Double = 60.0
300
297
private const val SIXTY_FPS : Double = 60.0
301
- private const val SIXTEEN_MS_NS : Long = 16666666
302
298
}
303
299
}
0 commit comments