99import io .sentry .protocol .SentryId ;
1010import java .util .HashMap ;
1111import java .util .Map ;
12+ import java .util .WeakHashMap ;
1213import java .util .concurrent .ConcurrentHashMap ;
1314import org .jetbrains .annotations .NotNull ;
1415import org .jetbrains .annotations .Nullable ;
@@ -26,6 +27,8 @@ public final class ActivityFramesTracker {
2627
2728 private final @ NotNull Map <SentryId , Map <String , @ NotNull MeasurementValue >>
2829 activityMeasurements = new ConcurrentHashMap <>();
30+ private final @ NotNull Map <Activity , FrameCounts > frameCountAtStartSnapshots =
31+ new WeakHashMap <>();
2932
3033 public ActivityFramesTracker (final @ NotNull LoadClass loadClass , final @ Nullable ILogger logger ) {
3134 androidXAvailable =
@@ -54,33 +57,31 @@ public synchronized void addActivity(final @NotNull Activity activity) {
5457 return ;
5558 }
5659 frameMetricsAggregator .add (activity );
60+ snapshotFrameCountsAtStart (activity );
5761 }
5862
59- @ SuppressWarnings ("NullAway" )
60- public synchronized void setMetrics (
61- final @ NotNull Activity activity , final @ NotNull SentryId sentryId ) {
63+ private void snapshotFrameCountsAtStart (final @ NotNull Activity activity ) {
64+ FrameCounts frameCounts = calculateCurrentFrameCounts ();
65+ if (frameCounts != null ) {
66+ frameCountAtStartSnapshots .put (activity , frameCounts );
67+ }
68+ }
69+
70+ private @ Nullable FrameCounts calculateCurrentFrameCounts () {
6271 if (!isFrameMetricsAggregatorAvailable ()) {
63- return ;
72+ return null ;
6473 }
6574
75+ if (frameMetricsAggregator == null ) {
76+ return null ;
77+ }
78+ final @ Nullable SparseIntArray [] framesRates = frameMetricsAggregator .getMetrics ();
79+
6680 int totalFrames = 0 ;
6781 int slowFrames = 0 ;
6882 int frozenFrames = 0 ;
6983
70- SparseIntArray [] framesRates = null ;
71- try {
72- framesRates = frameMetricsAggregator .remove (activity );
73- } catch (Throwable ignored ) {
74- // throws IllegalArgumentException when attempting to remove OnFrameMetricsAvailableListener
75- // that was never added.
76- // there's no contains method.
77- // throws NullPointerException when attempting to remove OnFrameMetricsAvailableListener and
78- // there was no
79- // Observers, See
80- // https://android.googlesource.com/platform/frameworks/base/+/140ff5ea8e2d99edc3fbe63a43239e459334c76b
81- }
82-
83- if (framesRates != null ) {
84+ if (framesRates != null && framesRates .length > 0 ) {
8485 final SparseIntArray totalIndexArray = framesRates [FrameMetricsAggregator .TOTAL_INDEX ];
8586 if (totalIndexArray != null ) {
8687 for (int i = 0 ; i < totalIndexArray .size (); i ++) {
@@ -99,39 +100,101 @@ public synchronized void setMetrics(
99100 }
100101 }
101102
102- if (totalFrames == 0 && slowFrames == 0 && frozenFrames == 0 ) {
103+ return new FrameCounts (totalFrames , slowFrames , frozenFrames );
104+ }
105+
106+ @ SuppressWarnings ("NullAway" )
107+ public synchronized void setMetrics (
108+ final @ NotNull Activity activity , final @ NotNull SentryId transactionId ) {
109+ if (!isFrameMetricsAggregatorAvailable ()) {
110+ return ;
111+ }
112+
113+ try {
114+ // NOTE: removing an activity does not reset the frame counts, only reset() does
115+ frameMetricsAggregator .remove (activity );
116+ } catch (Throwable ignored ) {
117+ // throws IllegalArgumentException when attempting to remove OnFrameMetricsAvailableListener
118+ // that was never added.
119+ // there's no contains method.
120+ // throws NullPointerException when attempting to remove OnFrameMetricsAvailableListener and
121+ // there was no
122+ // Observers, See
123+ // https://android.googlesource.com/platform/frameworks/base/+/140ff5ea8e2d99edc3fbe63a43239e459334c76b
124+ }
125+
126+ final @ Nullable FrameCounts frameCounts = diffFrameCountsAtEnd (activity );
127+
128+ if (frameCounts == null
129+ || (frameCounts .totalFrames == 0
130+ && frameCounts .slowFrames == 0
131+ && frameCounts .frozenFrames == 0 )) {
103132 return ;
104133 }
105134
106- final MeasurementValue tfValues = new MeasurementValue (totalFrames , MeasurementUnit .NONE );
107- final MeasurementValue sfValues = new MeasurementValue (slowFrames , MeasurementUnit .NONE );
108- final MeasurementValue ffValues = new MeasurementValue (frozenFrames , MeasurementUnit .NONE );
135+ final MeasurementValue tfValues =
136+ new MeasurementValue (frameCounts .totalFrames , MeasurementUnit .NONE );
137+ final MeasurementValue sfValues =
138+ new MeasurementValue (frameCounts .slowFrames , MeasurementUnit .NONE );
139+ final MeasurementValue ffValues =
140+ new MeasurementValue (frameCounts .frozenFrames , MeasurementUnit .NONE );
109141 final Map <String , @ NotNull MeasurementValue > measurements = new HashMap <>();
110142 measurements .put ("frames_total" , tfValues );
111143 measurements .put ("frames_slow" , sfValues );
112144 measurements .put ("frames_frozen" , ffValues );
113145
114- activityMeasurements .put (sentryId , measurements );
146+ activityMeasurements .put (transactionId , measurements );
147+ }
148+
149+ private @ Nullable FrameCounts diffFrameCountsAtEnd (final @ NotNull Activity activity ) {
150+ @ Nullable final FrameCounts frameCountsAtStart = frameCountAtStartSnapshots .remove (activity );
151+ if (frameCountsAtStart == null ) {
152+ return null ;
153+ }
154+
155+ @ Nullable final FrameCounts frameCountsAtEnd = calculateCurrentFrameCounts ();
156+ if (frameCountsAtEnd == null ) {
157+ return null ;
158+ }
159+
160+ final int diffTotalFrames = frameCountsAtEnd .totalFrames - frameCountsAtStart .totalFrames ;
161+ final int diffSlowFrames = frameCountsAtEnd .slowFrames - frameCountsAtStart .slowFrames ;
162+ final int diffFrozenFrames = frameCountsAtEnd .frozenFrames - frameCountsAtStart .frozenFrames ;
163+
164+ return new FrameCounts (diffTotalFrames , diffSlowFrames , diffFrozenFrames );
115165 }
116166
117167 @ Nullable
118168 public synchronized Map <String , @ NotNull MeasurementValue > takeMetrics (
119- final @ NotNull SentryId sentryId ) {
169+ final @ NotNull SentryId transactionId ) {
120170 if (!isFrameMetricsAggregatorAvailable ()) {
121171 return null ;
122172 }
123173
124174 final Map <String , @ NotNull MeasurementValue > stringMeasurementValueMap =
125- activityMeasurements .get (sentryId );
126- activityMeasurements .remove (sentryId );
175+ activityMeasurements .get (transactionId );
176+ activityMeasurements .remove (transactionId );
127177 return stringMeasurementValueMap ;
128178 }
129179
130180 @ SuppressWarnings ("NullAway" )
131181 public synchronized void stop () {
132182 if (isFrameMetricsAggregatorAvailable ()) {
133183 frameMetricsAggregator .stop ();
184+ frameMetricsAggregator .reset ();
134185 }
135186 activityMeasurements .clear ();
136187 }
188+
189+ private static final class FrameCounts {
190+ private final int totalFrames ;
191+ private final int slowFrames ;
192+ private final int frozenFrames ;
193+
194+ private FrameCounts (final int totalFrames , final int slowFrames , final int frozenFrames ) {
195+ this .totalFrames = totalFrames ;
196+ this .slowFrames = slowFrames ;
197+ this .frozenFrames = frozenFrames ;
198+ }
199+ }
137200}
0 commit comments