33import android .app .Activity ;
44import android .app .Application ;
55import android .content .ContentProvider ;
6+ import android .os .Build ;
67import android .os .Bundle ;
78import android .os .Handler ;
89import android .os .Looper ;
10+ import android .os .MessageQueue ;
911import android .os .SystemClock ;
1012import androidx .annotation .NonNull ;
1113import androidx .annotation .Nullable ;
@@ -65,7 +67,7 @@ public enum AppStartType {
6567 return ContextUtils .isForegroundImportance ();
6668 }
6769 });
68- private volatile long firstPostUptimeMillis = -1 ;
70+ private volatile long firstIdle = -1 ;
6971
7072 private final @ NotNull TimeSpan appStartSpan ;
7173 private final @ NotNull TimeSpan sdkInitTimeSpan ;
@@ -221,13 +223,13 @@ public long getClassLoadedUptimeMs() {
221223 }
222224
223225 @ TestOnly
224- void setFirstPostUptimeMillis (final long firstPostUptimeMillis ) {
225- this .firstPostUptimeMillis = firstPostUptimeMillis ;
226+ void setFirstIdle (final long firstIdle ) {
227+ this .firstIdle = firstIdle ;
226228 }
227229
228230 @ TestOnly
229- long getFirstPostUptimeMillis () {
230- return firstPostUptimeMillis ;
231+ long getFirstIdle () {
232+ return firstIdle ;
231233 }
232234
233235 @ TestOnly
@@ -247,12 +249,12 @@ public void clear() {
247249 }
248250 appStartContinuousProfiler = null ;
249251 appStartSamplingDecision = null ;
250- appLaunchedInForeground .setValue ( false );
252+ appLaunchedInForeground .resetValue ( );
251253 isCallbackRegistered = false ;
252254 shouldSendStartMeasurements = true ;
253255 firstDrawDone .set (false );
254256 activeActivitiesCounter .set (0 );
255- firstPostUptimeMillis = -1 ;
257+ firstIdle = -1 ;
256258 }
257259
258260 public @ Nullable ITransactionProfiler getAppStartProfiler () {
@@ -320,7 +322,8 @@ public static void onApplicationPostCreate(final @NotNull Application applicatio
320322 }
321323
322324 /**
323- * Register a callback to check if an activity was started after the application was created
325+ * Register a callback to check if an activity was started after the application was created. Must
326+ * be called from the main thread.
324327 *
325328 * @param application The application object to register the callback to
326329 */
@@ -331,40 +334,52 @@ public void registerLifecycleCallbacks(final @NotNull Application application) {
331334 isCallbackRegistered = true ;
332335 appLaunchedInForeground .resetValue ();
333336 application .registerActivityLifecycleCallbacks (instance );
334- // We post on the main thread a task to post a check on the main thread. On Pixel devices
335- // (possibly others) the first task posted on the main thread is called before the
336- // Activity.onCreate callback. This is a workaround for that, so that the Activity.onCreate
337- // callback is called before the application one.
338- new Handler (Looper .getMainLooper ())
339- .post (
340- new Runnable () {
341- @ Override
342- public void run () {
343- firstPostUptimeMillis = SystemClock .uptimeMillis ();
344- checkCreateTimeOnMain ();
345- }
346- });
337+
338+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
339+ Looper .getMainLooper ()
340+ .getQueue ()
341+ .addIdleHandler (
342+ new MessageQueue .IdleHandler () {
343+ @ Override
344+ public boolean queueIdle () {
345+ firstIdle = SystemClock .uptimeMillis ();
346+ checkCreateTimeOnMain ();
347+ return false ;
348+ }
349+ });
350+ } else {
351+ // We post on the main thread a task to post a check on the main thread. On Pixel devices
352+ // (possibly others) the first task posted on the main thread is called before the
353+ // Activity.onCreate callback. This is a workaround for that, so that the Activity.onCreate
354+ // callback is called before the application one.
355+ final Handler handler = new Handler (Looper .getMainLooper ());
356+ handler .post (
357+ new Runnable () {
358+ @ Override
359+ public void run () {
360+ // not technically correct, but close enough for pre-M
361+ firstIdle = SystemClock .uptimeMillis ();
362+ handler .post (() -> checkCreateTimeOnMain ());
363+ }
364+ });
365+ }
347366 }
348367
349368 private void checkCreateTimeOnMain () {
350- new Handler (Looper .getMainLooper ())
351- .post (
352- () -> {
353- // if no activity has ever been created, app was launched in background
354- if (activeActivitiesCounter .get () == 0 ) {
355- appLaunchedInForeground .setValue (false );
356-
357- // we stop the app start profilers, as they are useless and likely to timeout
358- if (appStartProfiler != null && appStartProfiler .isRunning ()) {
359- appStartProfiler .close ();
360- appStartProfiler = null ;
361- }
362- if (appStartContinuousProfiler != null && appStartContinuousProfiler .isRunning ()) {
363- appStartContinuousProfiler .close (true );
364- appStartContinuousProfiler = null ;
365- }
366- }
367- });
369+ // if no activity has ever been created, app was launched in background
370+ if (activeActivitiesCounter .get () == 0 ) {
371+ appLaunchedInForeground .setValue (false );
372+
373+ // we stop the app start profilers, as they are useless and likely to timeout
374+ if (appStartProfiler != null && appStartProfiler .isRunning ()) {
375+ appStartProfiler .close ();
376+ appStartProfiler = null ;
377+ }
378+ if (appStartContinuousProfiler != null && appStartContinuousProfiler .isRunning ()) {
379+ appStartContinuousProfiler .close (true );
380+ appStartContinuousProfiler = null ;
381+ }
382+ }
368383 }
369384
370385 @ Override
@@ -389,10 +404,7 @@ public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle saved
389404 applicationOnCreate .reset ();
390405 } else if (savedInstanceState != null ) {
391406 appStartType = AppStartType .WARM ;
392- } else if (firstPostUptimeMillis != -1
393- && activityCreatedUptimeMillis > firstPostUptimeMillis ) {
394- // Application creation always queues Activity creation
395- // So if Activity is created after our first measured post, it's a warm start
407+ } else if (firstIdle != -1 && activityCreatedUptimeMillis > firstIdle ) {
396408 appStartType = AppStartType .WARM ;
397409 } else {
398410 appStartType = AppStartType .COLD ;
0 commit comments