55import androidx .core .app .FrameMetricsAggregator ;
66import io .sentry .ILogger ;
77import io .sentry .MeasurementUnit ;
8+ import io .sentry .SentryLevel ;
9+ import io .sentry .android .core .internal .util .MainThreadChecker ;
810import io .sentry .protocol .MeasurementValue ;
911import io .sentry .protocol .SentryId ;
1012import java .util .HashMap ;
@@ -30,21 +32,37 @@ public final class ActivityFramesTracker {
3032 private final @ NotNull Map <Activity , FrameCounts > frameCountAtStartSnapshots =
3133 new WeakHashMap <>();
3234
33- public ActivityFramesTracker (final @ NotNull LoadClass loadClass , final @ Nullable ILogger logger ) {
35+ private final @ Nullable ILogger logger ;
36+ private final @ NotNull MainLooperHandler handler ;
37+
38+ public ActivityFramesTracker (
39+ final @ NotNull LoadClass loadClass ,
40+ final @ Nullable ILogger logger ,
41+ final @ NotNull MainLooperHandler handler ) {
3442 androidXAvailable =
3543 loadClass .isClassAvailable ("androidx.core.app.FrameMetricsAggregator" , logger );
3644 if (androidXAvailable ) {
3745 frameMetricsAggregator = new FrameMetricsAggregator ();
3846 }
47+ this .logger = logger ;
48+ this .handler = handler ;
49+ }
50+
51+ public ActivityFramesTracker (final @ NotNull LoadClass loadClass , final @ Nullable ILogger logger ) {
52+ this (loadClass , logger , new MainLooperHandler ());
3953 }
4054
4155 public ActivityFramesTracker (final @ NotNull LoadClass loadClass ) {
4256 this (loadClass , null );
4357 }
4458
4559 @ TestOnly
46- ActivityFramesTracker (final @ Nullable FrameMetricsAggregator frameMetricsAggregator ) {
60+ ActivityFramesTracker (
61+ final @ Nullable FrameMetricsAggregator frameMetricsAggregator ,
62+ final @ NotNull MainLooperHandler handler ) {
4763 this .frameMetricsAggregator = frameMetricsAggregator ;
64+ this .logger = null ;
65+ this .handler = handler ;
4866 }
4967
5068 private boolean isFrameMetricsAggregatorAvailable () {
@@ -56,7 +74,8 @@ public synchronized void addActivity(final @NotNull Activity activity) {
5674 if (!isFrameMetricsAggregatorAvailable ()) {
5775 return ;
5876 }
59- frameMetricsAggregator .add (activity );
77+
78+ runSafelyOnUiThread (() -> frameMetricsAggregator .add (activity ), "FrameMetricsAggregator.add" );
6079 snapshotFrameCountsAtStart (activity );
6180 }
6281
@@ -75,6 +94,7 @@ private void snapshotFrameCountsAtStart(final @NotNull Activity activity) {
7594 if (frameMetricsAggregator == null ) {
7695 return null ;
7796 }
97+
7898 final @ Nullable SparseIntArray [] framesRates = frameMetricsAggregator .getMetrics ();
7999
80100 int totalFrames = 0 ;
@@ -110,18 +130,18 @@ public synchronized void setMetrics(
110130 return ;
111131 }
112132
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- }
133+ // NOTE: removing an activity does not reset the frame counts, only reset() does
134+ // throws IllegalArgumentException when attempting to remove
135+ // OnFrameMetricsAvailableListener
136+ // that was never added.
137+ // there's no contains method.
138+ // throws NullPointerException when attempting to remove
139+ // OnFrameMetricsAvailableListener and
140+ // there was no
141+ // Observers, See
142+ // https://android.googlesource.com/platform/frameworks/base/+/140ff5ea8e2d99edc3fbe63a43239e459334c76b
143+ runSafelyOnUiThread (
144+ () -> frameMetricsAggregator . remove ( activity ), "FrameMetricsAggregator.remove" );
125145
126146 final @ Nullable FrameCounts frameCounts = diffFrameCountsAtEnd (activity );
127147
@@ -180,12 +200,35 @@ public synchronized void setMetrics(
180200 @ SuppressWarnings ("NullAway" )
181201 public synchronized void stop () {
182202 if (isFrameMetricsAggregatorAvailable ()) {
183- frameMetricsAggregator .stop ();
203+ runSafelyOnUiThread (() -> frameMetricsAggregator .stop (), "FrameMetricsAggregator.stop" );
184204 frameMetricsAggregator .reset ();
185205 }
186206 activityMeasurements .clear ();
187207 }
188208
209+ private void runSafelyOnUiThread (final Runnable runnable , final String tag ) {
210+ try {
211+ if (MainThreadChecker .isMainThread ()) {
212+ runnable .run ();
213+ } else {
214+ handler .post (
215+ () -> {
216+ try {
217+ runnable .run ();
218+ } catch (Throwable t ) {
219+ if (logger != null ) {
220+ logger .log (SentryLevel .ERROR , "Failed to execute " + tag , t );
221+ }
222+ }
223+ });
224+ }
225+ } catch (Throwable t ) {
226+ if (logger != null ) {
227+ logger .log (SentryLevel .ERROR , "Failed to execute " + tag , t );
228+ }
229+ }
230+ }
231+
189232 private static final class FrameCounts {
190233 private final int totalFrames ;
191234 private final int slowFrames ;
0 commit comments