Skip to content

Commit 0a218d4

Browse files
Add frame metrics to fragment screen traces.
1 parent 011a0f3 commit 0a218d4

File tree

3 files changed

+116
-35
lines changed

3 files changed

+116
-35
lines changed

firebase-perf/src/main/java/com/google/firebase/perf/application/AppStateMonitor.java

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import com.google.android.gms.common.util.VisibleForTesting;
2727
import com.google.firebase.perf.config.ConfigResolver;
2828
import com.google.firebase.perf.logging.AndroidLogger;
29+
import com.google.firebase.perf.metrics.FrameMetricsCalculator;
30+
import com.google.firebase.perf.metrics.FrameMetricsCalculator.FrameMetrics;
2931
import com.google.firebase.perf.metrics.Trace;
3032
import com.google.firebase.perf.session.SessionManager;
3133
import com.google.firebase.perf.transport.TransportManager;
@@ -336,9 +338,6 @@ private void sendScreenTrace(Activity activity) {
336338
}
337339
activityToScreenTraceMap.remove(activity);
338340

339-
int totalFrames = 0;
340-
int slowFrames = 0;
341-
int frozenFrames = 0;
342341
/**
343342
* Resets the metrics data and returns the currently-collected metrics. Note that {@link
344343
* FrameMetricsAggregator#reset()} will not stop recording for the activity. The reason of using
@@ -352,48 +351,37 @@ private void sendScreenTrace(Activity activity) {
352351
logger.debug("View not hardware accelerated. Unable to collect screen trace.");
353352
}
354353
SparseIntArray[] arr = frameMetricsAggregator.reset();
355-
if (arr != null) {
356-
SparseIntArray frameTimes = arr[FrameMetricsAggregator.TOTAL_INDEX];
357-
if (frameTimes != null) {
358-
for (int i = 0; i < frameTimes.size(); i++) {
359-
int frameTime = frameTimes.keyAt(i);
360-
int numFrames = frameTimes.valueAt(i);
361-
totalFrames += numFrames;
362-
if (frameTime > Constants.FROZEN_FRAME_TIME) {
363-
// Frozen frames mean the app appear frozen. The recommended thresholds is 700ms
364-
frozenFrames += numFrames;
365-
}
366-
if (frameTime > Constants.SLOW_FRAME_TIME) {
367-
// Slow frames are anything above 16ms (i.e. 60 frames/second)
368-
slowFrames += numFrames;
369-
}
370-
}
371-
}
372-
}
373-
if (totalFrames == 0 && slowFrames == 0 && frozenFrames == 0) {
354+
FrameMetrics frameMetrics = FrameMetricsCalculator.calculateFrameMetrics(arr);
355+
356+
if (frameMetrics.getTotalFrames() == 0
357+
&& frameMetrics.getSlowFrames() == 0
358+
&& frameMetrics.getFrozenFrames() == 0) {
374359
// All metrics are zero, no need to send screen trace.
375-
// return;
360+
return;
376361
}
377-
// Only incrementMetric if corresponding metric is non-zero.
378-
if (totalFrames > 0) {
379-
screenTrace.putMetric(Constants.CounterNames.FRAMES_TOTAL.toString(), totalFrames);
362+
// Only putMetric if corresponding metric is non-zero.
363+
if (frameMetrics.getTotalFrames() > 0) {
364+
screenTrace.putMetric(
365+
Constants.CounterNames.FRAMES_TOTAL.toString(), frameMetrics.getTotalFrames());
380366
}
381-
if (slowFrames > 0) {
382-
screenTrace.putMetric(Constants.CounterNames.FRAMES_SLOW.toString(), slowFrames);
367+
if (frameMetrics.getSlowFrames() > 0) {
368+
screenTrace.putMetric(
369+
Constants.CounterNames.FRAMES_SLOW.toString(), frameMetrics.getSlowFrames());
383370
}
384-
if (frozenFrames > 0) {
385-
screenTrace.putMetric(Constants.CounterNames.FRAMES_FROZEN.toString(), frozenFrames);
371+
if (frameMetrics.getFrozenFrames() > 0) {
372+
screenTrace.putMetric(
373+
Constants.CounterNames.FRAMES_FROZEN.toString(), frameMetrics.getFrozenFrames());
386374
}
387375
if (Utils.isDebugLoggingEnabled(activity.getApplicationContext())) {
388376
logger.debug(
389377
"sendScreenTrace name:"
390378
+ getScreenTraceName(activity)
391379
+ " _fr_tot:"
392-
+ totalFrames
380+
+ frameMetrics.getTotalFrames()
393381
+ " _fr_slo:"
394-
+ slowFrames
382+
+ frameMetrics.getSlowFrames()
395383
+ " _fr_fzn:"
396-
+ frozenFrames);
384+
+ frameMetrics.getFrozenFrames());
397385
}
398386
// Stop and record trace
399387
screenTrace.stop();

firebase-perf/src/main/java/com/google/firebase/perf/application/FragmentStateMonitor.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import androidx.fragment.app.FragmentManager;
2121
import com.google.android.gms.common.util.VisibleForTesting;
2222
import com.google.firebase.perf.logging.AndroidLogger;
23+
import com.google.firebase.perf.metrics.FrameMetricsCalculator;
24+
import com.google.firebase.perf.metrics.FrameMetricsCalculator.FrameMetrics;
2325
import com.google.firebase.perf.metrics.Trace;
2426
import com.google.firebase.perf.transport.TransportManager;
2527
import com.google.firebase.perf.util.Clock;
@@ -29,6 +31,8 @@
2931
public class FragmentStateMonitor extends FragmentManager.FragmentLifecycleCallbacks {
3032
private static final AndroidLogger logger = AndroidLogger.getInstance();
3133
private final WeakHashMap<Fragment, Trace> fragmentToTraceMap = new WeakHashMap<>();
34+
private final WeakHashMap<Fragment, FrameMetricsCalculator.FrameMetrics> fragmentToMetricsMap =
35+
new WeakHashMap<>();
3236
private final Clock clock;
3337
private final TransportManager transportManager;
3438
private final AppStateMonitor appStateMonitor;
@@ -73,8 +77,11 @@ public void onFragmentResumed(@NonNull FragmentManager fm, @NonNull Fragment f)
7377
fragmentTrace.putAttribute(
7478
Constants.ACTIVITY_ATTRIBUTE_KEY, f.getActivity().getClass().getSimpleName());
7579
}
76-
7780
fragmentToTraceMap.put(f, fragmentTrace);
81+
82+
FrameMetrics frameMetrics =
83+
FrameMetricsCalculator.calculateFrameMetrics(this.frameMetricsAggregator.getMetrics());
84+
fragmentToMetricsMap.put(f, frameMetrics);
7885
}
7986

8087
@Override
@@ -90,8 +97,30 @@ public void onFragmentPaused(@NonNull FragmentManager fm, @NonNull Fragment f) {
9097

9198
Trace fragmentTrace = fragmentToTraceMap.get(f);
9299
fragmentToTraceMap.remove(f);
100+
FrameMetrics preFrameMetrics = fragmentToMetricsMap.get(f);
101+
fragmentToMetricsMap.remove(f);
102+
103+
FrameMetrics curFrameMetrics =
104+
FrameMetricsCalculator.calculateFrameMetrics(this.frameMetricsAggregator.getMetrics());
93105

94-
// TODO: Add frame metrics
106+
int totalFrames = curFrameMetrics.getTotalFrames() - preFrameMetrics.getTotalFrames();
107+
int slowFrames = curFrameMetrics.getSlowFrames() - preFrameMetrics.getSlowFrames();
108+
int frozenFrames = curFrameMetrics.getFrozenFrames() - preFrameMetrics.getFrozenFrames();
109+
110+
if (totalFrames == 0 && slowFrames == 0 && frozenFrames == 0) {
111+
// All metrics are zero, no need to send screen trace.
112+
return;
113+
}
114+
// Only putMetric if corresponding metric is non-zero.
115+
if (totalFrames > 0) {
116+
fragmentTrace.putMetric(Constants.CounterNames.FRAMES_TOTAL.toString(), totalFrames);
117+
}
118+
if (slowFrames > 0) {
119+
fragmentTrace.putMetric(Constants.CounterNames.FRAMES_SLOW.toString(), slowFrames);
120+
}
121+
if (frozenFrames > 0) {
122+
fragmentTrace.putMetric(Constants.CounterNames.FRAMES_FROZEN.toString(), frozenFrames);
123+
}
95124

96125
fragmentTrace.stop();
97126
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.google.firebase.perf.metrics;
2+
3+
import android.util.SparseIntArray;
4+
import androidx.core.app.FrameMetricsAggregator;
5+
import com.google.firebase.perf.util.Constants;
6+
7+
public class FrameMetricsCalculator {
8+
public static class FrameMetrics {
9+
int totalFrames = 0;
10+
int slowFrames = 0;
11+
int frozenFrames = 0;
12+
13+
FrameMetrics(int totalFrames, int slowFrames, int frozenFrames) {
14+
this.totalFrames = totalFrames;
15+
this.slowFrames = slowFrames;
16+
this.frozenFrames = frozenFrames;
17+
}
18+
19+
public int getFrozenFrames() {
20+
return frozenFrames;
21+
}
22+
23+
public int getSlowFrames() {
24+
return slowFrames;
25+
}
26+
27+
public int getTotalFrames() {
28+
return totalFrames;
29+
}
30+
}
31+
32+
/**
33+
* Calculate total frames, slow frames, and frozen frames from arr.
34+
*
35+
* @param arr the metrics data collected by {@link FrameMetricsAggregator}
36+
* @return the frame metrics
37+
*/
38+
public static FrameMetrics calculateFrameMetrics(SparseIntArray[] arr) {
39+
int totalFrames = 0;
40+
int slowFrames = 0;
41+
int frozenFrames = 0;
42+
43+
if (arr != null) {
44+
SparseIntArray frameTimes = arr[FrameMetricsAggregator.TOTAL_INDEX];
45+
if (frameTimes != null) {
46+
for (int i = 0; i < frameTimes.size(); i++) {
47+
int frameTime = frameTimes.keyAt(i);
48+
int numFrames = frameTimes.valueAt(i);
49+
totalFrames += numFrames;
50+
if (frameTime > Constants.FROZEN_FRAME_TIME) {
51+
// Frozen frames mean the app appear frozen. The recommended thresholds is 700ms
52+
frozenFrames += numFrames;
53+
}
54+
if (frameTime > Constants.SLOW_FRAME_TIME) {
55+
// Slow frames are anything above 16ms (i.e. 60 frames/second)
56+
slowFrames += numFrames;
57+
}
58+
}
59+
}
60+
}
61+
// Only incrementMetric if corresponding metric is non-zero.
62+
return new FrameMetrics(totalFrames, slowFrames, frozenFrames);
63+
}
64+
}

0 commit comments

Comments
 (0)