Skip to content

Commit ef2b3bf

Browse files
committed
Switch to MessageQueue.IdleHandler
As firstPost doesn't work on Android 34+ devices
1 parent 7034d95 commit ef2b3bf

File tree

4 files changed

+147
-135
lines changed

4 files changed

+147
-135
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import android.app.Activity;
44
import android.app.Application;
55
import android.content.ContentProvider;
6+
import android.os.Build;
67
import android.os.Bundle;
78
import android.os.Handler;
89
import android.os.Looper;
10+
import android.os.MessageQueue;
911
import android.os.SystemClock;
1012
import androidx.annotation.NonNull;
1113
import 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

Comments
 (0)