appOpenedTime always uses mainActivityStartTime: The difference between appStartedTimestamp (process creation) and mainActivityStartTime (MainActivity creation) is negligible (20-40ms). Using mainActivityStartTime consistently simplifies the code and fixes edge cases where the conditional logic couldn't distinguish between different launch scenarios (9c, 9f).
Description: User taps app icon → Fresh process starts → MainActivity instantiates
Result: COLD START ✅
Visual Diagram:
Process (alive 1hr) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝗮𝗽𝗽 𝗶𝗰𝗼𝗻
| | |
T+0ms ┿━━━ Process starts | |
| 𝗮𝗽𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝗦𝘁𝗮𝗿𝘁𝗲𝗱=𝟬 | |
| | |
T+10ms ├─ instantiateApplication() | |
T+15ms ├─ MainApplication.onCreate() | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| └─ Load RN, init modules | |
| | |
T+120ms 🟢 ├─ instantiateActivity() | |
┃ | | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝗳𝗮𝗹𝘀𝗲 (init still running) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ staticMainActivityCreationTime = 120 |
┃ | | |
T+120ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+160ms ┃ | onCreate() |
┃ | └─ API < 28 fallback not triggered
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+3500ms ┃ └─ initialize() complete | |
┃ ├─ Content renders |
┃ | |
┃ | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 120ms ║
║ TTI = [view visible time] - 120ms ║
╚══════════════════════════════════════════════════════════════════════╝
Description: App backgrounded → MainActivity destroyed & timestamp cleared → User returns → New MainActivity created.
Result: WARM START ✅
Visual Diagram:
Process (alive 1hr) MainActivity User
| | |
T-3600000ms | ● |
| | (created 1hr ago) |
| | |
[App backgrounded - MainActivity destroyed by Android]
| | |
T-3599950ms | onDestroy() |
| ├─ isChangingConfigurations = false
| └─ staticMainActivityCreationTime = null
| | |
| ✗ (destroyed) |
| | |
[User taps app icon]
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝗮𝗽𝗽 𝗶𝗰𝗼𝗻
| | |
T+0ms | | |
T+10ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝘁𝗿𝘂𝗲 (init already done) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟭𝟬 |
┃ | | |
T+10ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+60ms ┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+3500ms ┃ | ├─ Content renders |
┃ | | |
┃ | | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 10ms ║
║ TTI = [view visible time] - 10ms ║
╚══════════════════════════════════════════════════════════════════════╝
Description: User shares from another app → ShareActivity (excluded) opens → User navigates to main app → MainActivity instantiates.
Result: WARM START ✅
Visual Diagram:
Process (alive 1hr) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 "𝗦𝗵𝗮𝗿𝗲"
| | 𝗶𝗻 𝗮𝗻𝗼𝘁𝗵𝗲𝗿 𝗮𝗽𝗽
T+0ms | | |
| 𝗮𝗽𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝗦𝘁𝗮𝗿𝘁𝗲𝗱=𝟬 | |
| | |
T+15ms ├─ MainApplication.onCreate() | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| └─ Load RN, init modules | |
| | |
T+120ms ├─ instantiateActivity() | |
| ├─ endsWith(".MainActivity") = FALSE |
| └─ SKIPPED - No timestamp set | |
| | |
T+160ms | | 🖥️ Share sheet
| | visible
| | |
[User completes share, taps to open main app]
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝘁𝗼 𝗼𝗽𝗲𝗻
| | 𝗺𝗮𝗶𝗻 𝗮𝗽𝗽
| | |
T+5000ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝘁𝗿𝘂𝗲 (init done during share) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟱𝟬𝟬𝟬 |
┃ | | |
T+5000ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+5050ms ┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+8500ms ┃ | ├─ Content renders |
┃ | | |
┃ | | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 5000ms ║
║ TTI = [view visible time] - 5000ms ║
╚══════════════════════════════════════════════════════════════════════╝
Description: Notification arrives → Service processes headlessly → User taps 1 minute later → MainActivity instantiates .
Result: WARM START ✅
Visual Diagram:
Process (alive 1hr) MainActivity User
| | |
T+0ms | | |
| 𝗮𝗽𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝗦𝘁𝗮𝗿𝘁𝗲𝗱=𝟬 | |
| | |
T+15ms ├─ MainApplication.onCreate() | |
| | |
T+50ms ├─ instantiateService() | |
| └─ markServiceInstantiation() | |
| └─ serviceInstantiationBeforeActivity = true |
| | |
T+80ms ├─ MessagingService.onMessageReceived() |
| (headless, no UI) | |
| | |
[Process stays alive in background - no Activity]
| | |
| | |
| | |
[LATER: User taps notification]
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀
| | 𝗻𝗼𝘁𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻
| | |
T+60000ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝘁𝗿𝘂𝗲 (init done in background) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟲𝟬𝟬𝟬𝟬 |
┃ | | |
T+60000ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+60050ms┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+63500ms┃ | ├─ Content renders |
┃ | | |
┃ | | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 60000ms ║
║ TTI = [view visible time] - 60000ms ║
╚══════════════════════════════════════════════════════════════════════╝
Description: Notification arrives → Service instantiates → User taps notification immediately (before initialize() finishes) → MainActivity opens → User waits for initialize() to complete.
Result: COLD START ✅
Visual Diagram:
Process (alive 1hr) MainActivity User
| | |
| | 📱 𝗡𝗼𝘁𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻 𝗮𝗿𝗿𝗶𝘃𝗲𝘀
| | |
T+0ms | | |
| 𝗮𝗽𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝗦𝘁𝗮𝗿𝘁𝗲𝗱=𝟬 | |
| | |
T+15ms ├─ MainApplication.onCreate() | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| └─ Load RN, init modules | |
| | |
T+80ms ├─ instantiateService() | |
| └─ serviceInstantiationBeforeActivity = true |
| | |
T+100ms ├─ MessagingService.onMessageReceived() |
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝗻𝗼𝘁𝗶𝗳
| | 𝗶𝗺𝗺𝗲𝗱𝗶𝗮𝘁𝗲𝗹𝘆
| | |
T+120ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝗳𝗮𝗹𝘀𝗲 (init still running!) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟭𝟮𝟬 |
┃ | | |
T+120ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+160ms ┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+3500ms ┃ └─ initialize() complete | |
┃ ├─ Content renders |
┃ | |
┃ | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 120ms ║
║ appStartType = COLD (initialize not complete when MA opened) ║
║ TTI = [view visible time] - 120ms = ~3380ms ║
╚══════════════════════════════════════════════════════════════════════╝
Description: App already running → User taps notification → onNewIntent() called (not instantiateActivity) → Duplicate detection prevents TTI measurement (no new activity creation).
Result: NO TTI CALCULATED (duplicate detection)
Visual Diagram:
Process (alive) MainActivity User
| ● |
T-10000ms | staticMainActivityCreationTime |
| set previously |
| | |
| | |
| | 📱 𝗡𝗼𝘁𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻 𝗮𝗿𝗿𝗶𝘃𝗲𝘀
| | |
T+0ms | | |
├─ Notification arrives | |
| | |
T+10ms ├─ MessagingService.onMessageReceived() |
| └─ serviceInstantiationBeforeActivity = false |
| | |
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀
| | 𝗻𝗼𝘁𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻
T+5000ms | | |
| | |
T+5010ms | onNewIntent() |
| | |
| ├─ instantiateActivity() NOT called
| ├─ staticMainActivityCreationTime unchanged
| | |
T+5020ms | 𝗴𝗲𝘁𝗔𝗽𝗽𝗨𝗜𝗩𝗶𝗲𝘄𝗲𝗱=𝘁𝗿𝘂𝗲 |
| | |
| ├─ 𝗡𝗼 𝗮𝗻𝗮𝗹𝘆𝘁𝗶𝗰𝘀 𝘀𝗲𝗻𝘁 |
| | |
╔══════════════════════════════════════════════════════════════════════╗
║ NO TTI MEASUREMENT (duplicate detection blocks it) ║
║ appViewedByMainActivityTimes[OLD TIME] = true → returns TRUE ║
║ No analytics sent → Correct behavior ║
╚══════════════════════════════════════════════════════════════════════╝
Description: App running → User rotates device → onConfigurationChanged() called (activity not destroyed/recreated due to android:configChanges) → No TTI measurement (no lifecycle change).
Result: NO TTI CALCULATED (no activity recreation)
Visual Diagram:
Process (alive) MainActivity User
| ● |
| staticMainActivityCreationTime |
| unchanged |
| | |
| | 🔄 𝗨𝘀𝗲𝗿 𝗿𝗼𝘁𝗮𝘁𝗲𝘀
| | 𝗱𝗲𝘃𝗶𝗰𝗲
T+0ms | | |
| | |
T+10ms | onConfigurationChanged() |
| | |
| ├─ instantiateActivity() NOT called
| ├─ onDestroy() NOT called
| ├─ staticMainActivityCreationTime unchanged
| | |
T+50ms | Redraws view 🖥️ New orientation
╔══════════════════════════════════════════════════════════════════════╗
║ NO TTI MEASUREMENT (no activity lifecycle change) ║
║ android:configChanges prevents activity recreation ║
║ No impact on TTI tracking → Correct behavior ║
╚══════════════════════════════════════════════════════════════════════╝
Description: Incoming call → MainActivity opens first → IncomingCallActivity may appear as overlay/system UI (excluded) → User answers → MainActivity continues.
Result: COLD or WARM START ✅ (depends on whether initialize() finished during call)
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 📞 𝗜𝗻𝗰𝗼𝗺𝗶𝗻𝗴 𝗰𝗮𝗹𝗹
| | |
T+0ms | | |
𝗮𝗽𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝗦𝘁𝗮𝗿𝘁𝗲𝗱=𝟬 | |
| | |
T+15ms ├─ MainApplication.onCreate() | |
| | |
T+120ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝘁𝗿𝘂𝗲/𝗳𝗮𝗹𝘀𝗲 (COLD/WARM) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟭𝟮𝟬 |
┃ | | |
T+120ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+160ms ┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
| | |
| (IncomingCallActivity may | |
| appear as overlay/system | |
| UI, not tracked in TTI) | |
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗮𝗻𝘀𝘄𝗲𝗿𝘀 𝗰𝗮𝗹𝗹
| | |
T+3500ms ┃ └─ initialize() complete | |
┃ ├─ Content renders |
┃ | |
┃ | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appStartType = COLD/WARM (depends on initialize() completion) ║
║ appOpenedTime = 120ms ║
║ TTI = [view visible time] - 120ms ║
║ IncomingCallActivity (overlay/system UI) correctly EXCLUDED ║
╚══════════════════════════════════════════════════════════════════════╝
Description: User long-presses app icon → Process starts (on some phones, e.g., Samsung) or doesn't start (on others, e.g., Google Pixel) → Shortcuts menu appears → Various user actions follow.
9a: Long-press → Tap Shortcut (COLD)
Description: User long-presses → Process starts → Shortcuts menu appears → User taps a shortcut option → MainActivity created with discord_shortcut Intent extra.
Result: COLD START ✅
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗹𝗼𝗻𝗴 𝗽𝗿𝗲𝘀𝘀𝗲𝘀 𝗶𝗰𝗼𝗻
| | (Samsung: triggers process start;
| | Pixel: process doesn't start)
| | |
T+0ms ┿━━━ Process starts | |
| (Samsung: starts here; | |
| Pixel: starts later when | |
| user taps shortcut) | |
| 𝗮𝗽𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝗦𝘁𝗮𝗿𝘁𝗲𝗱=𝟬 | |
| | |
T+15ms ├─ MainApplication.onCreate() | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| | |
T+100ms | | 🖥️ Shortcuts menu appears
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝘀𝗵𝗼𝗿𝘁𝗰𝘂𝘁
| | |
T+120ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ Intent has discord_shortcut extra |
┃ | ├─ wasLaunchedViaShortcut = true |
┃ | ├─ wasInitializeComplete = false (COLD) |
┃ | └─ staticMainActivityCreationTime = 120 |
┃ | | |
T+120ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+160ms ┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+3500ms ┃ └─ initialize() complete | |
┃ ├─ Content renders |
┃ | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 120ms ║
║ TTI = [view visible time] - 120ms ║
╚══════════════════════════════════════════════════════════════════════╝
9b: Long-press → Tap Shortcut (WARM)
Description: User long-presses → Process starts → User waits/browses shortcuts → User taps shortcut after initialize() completes.
Result: WARM START ✅
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗹𝗼𝗻𝗴 𝗽𝗿𝗲𝘀𝘀𝗲𝘀 𝗶𝗰𝗼𝗻
| | (Samsung: process starts here;
| | Pixel: process doesn't start)
| | |
T+0ms ┿━━━ Process starts | |
| (Samsung only; Pixel: | |
| starts later when | |
| user taps shortcut) | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| | |
T+100ms | | 🖥️ Shortcuts menu appears
| | |
| | ⏱️ 𝗨𝘀𝗲𝗿 𝘄𝗮𝗶𝘁𝘀
| | |
T+3500ms └─ initialize() complete | |
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝘀𝗵𝗼𝗿𝘁𝗰𝘂𝘁
| | |
T+4000ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ wasLaunchedViaShortcut = true |
┃ | ├─ wasInitializeComplete = true (WARM) |
┃ | └─ staticMainActivityCreationTime = 4000 |
┃ | | |
T+4000ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+4100ms┃ | ├─ Content renders |
┃ | | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 4000ms ║
║ TTI = [view visible time] - 4000ms ║
╚══════════════════════════════════════════════════════════════════════╝
9c: Long-press → Tap Regular Icon (COLD) - RARE
Description: User long-presses → Process starts → Shortcuts menu appears → User dismisses menu or taps regular icon within 100ms after the shortcuts show up, instead of tapping one of the shortcuts → MainActivity created by tapping app icon (no discord_shortcut extra).
Result: COLD START ✅
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗹𝗼𝗻𝗴 𝗽𝗿𝗲𝘀𝘀𝗲𝘀 𝗶𝗰𝗼𝗻
| | (Samsung: triggers process start;
| | Pixel: process doesn't start)
| | |
T+0ms ┿━━━ Process starts | |
| (Samsung: starts here; | |
| Pixel: starts later when | |
| user taps icon) | |
| 𝗮𝗽𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝗦𝘁𝗮𝗿𝘁𝗲𝗱=𝟬 | |
| | |
T+15ms ├─ MainApplication.onCreate() | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| | |
T+100ms | | 🖥️ Shortcuts menu appears
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝗿𝗲𝗴𝘂𝗹𝗮𝗿 𝗶𝗰𝗼𝗻
| | (no discord_shortcut extra)
| | |
T+120ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ Intent has NO discord_shortcut extra |
┃ | ├─ wasLaunchedViaShortcut = false |
┃ | ├─ wasInitializeComplete = false (COLD) |
┃ | └─ staticMainActivityCreationTime = 120 |
┃ | | |
T+120ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+160ms ┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+3500ms ┃ └─ initialize() complete | |
┃ ├─ Content renders |
┃ | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 120ms ║
║ TTI = [view visible time] - 120ms ║
╚══════════════════════════════════════════════════════════════════════╝
9d: Long-press → Tap Regular Icon (WARM)
Description: User long-presses → Process starts → User waits → User taps app icon icon after initialize() completes.
Result: WARM START ✅
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗹𝗼𝗻𝗴 𝗽𝗿𝗲𝘀𝘀𝗲𝘀 𝗶𝗰𝗼𝗻
| | (Samsung: process starts here;
| | Pixel: process doesn't start)
| | |
T+0ms ┿━━━ Process starts | |
| (Samsung only; Pixel: | |
| starts later when | |
| user taps icon) | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| | |
T+100ms | | 🖥️ Shortcuts menu appears
| | |
| | ⏱️ 𝗨𝘀𝗲𝗿 𝘄𝗮𝗶𝘁𝘀
| | |
T+3500ms └─ initialize() complete | |
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝗿𝗲𝗴𝘂𝗹𝗮𝗿 𝗶𝗰𝗼𝗻
| | |
T+4000ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ Intent has NO discord_shortcut extra |
┃ | ├─ wasLaunchedViaShortcut = false |
┃ | ├─ wasInitializeComplete = true (WARM) |
┃ | └─ staticMainActivityCreationTime = 4000 |
┃ | | |
T+4000ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+4100ms┃ | ├─ Content renders |
┃ | | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 4000ms ║
║ TTI = [view visible time] - 4000ms ║
╚══════════════════════════════════════════════════════════════════════╝
9e: Long-press → Notification Tap
Description: User long-presses → Process starts → User taps notification → Service/receiver created before MainActivity.
Result: COLD or WARM START ✅
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗹𝗼𝗻𝗴 𝗽𝗿𝗲𝘀𝘀𝗲𝘀 𝗶𝗰𝗼𝗻
| | (Samsung: process starts here;
| | Pixel: process doesn't start)
| | |
T+0ms ┿━━━ Process starts | |
| (Samsung only; Pixel: | |
| starts later when | |
| user taps notification) | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| | |
T+100ms | | 🖥️ Shortcuts menu appears
| | |
| | 📱 𝗡𝗼𝘁𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻 𝗮𝗿𝗿𝗶𝘃𝗲𝘀
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘁𝗮𝗽𝘀 𝗻𝗼𝘁𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻
| | |
T+5000ms ├─ instantiateService() | |
| └─ serviceInstantiationBeforeActivity = true |
| | |
T+5010ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝘁𝗿𝘂𝗲/𝗳𝗮𝗹𝘀𝗲 (COLD/WARM) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟱𝟬𝟭𝟬 |
┃ | | |
T+5010ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+5060ms┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+8510ms┃ | ├─ Content renders |
┃ | | |
┃ | | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 5010ms ║
║ TTI = [view visible time] - 5010ms ║
╚══════════════════════════════════════════════════════════════════════╝
9f: Long-press → Share Activity
Description: User long-presses → Process starts → User shares from another app → ShareActivity created before MainActivity.
Result: WARM START ✅
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗹𝗼𝗻𝗴 𝗽𝗿𝗲𝘀𝘀𝗲𝘀 𝗶𝗰𝗼𝗻
| | (Samsung: process starts here;
| | Pixel: process doesn't start)
| | |
T+0ms ┿━━━ Process starts | |
| (Samsung only; Pixel: | |
| starts later when | |
| user shares from app) | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| | |
T+100ms | | 🖥️ Shortcuts menu appears
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝘀𝗵𝗮𝗿𝗲𝘀 𝗳𝗿𝗼𝗺 𝗮𝗻𝗼𝘁𝗵𝗲𝗿 𝗮𝗽𝗽
| | |
T+5000ms ├─ instantiateActivity("ShareActivity") |
| ├─ endsWith(".MainActivity") = FALSE |
| └─ SKIPPED - No timestamp set | |
| | |
T+5050ms ├─ ShareActivity.onCreate() | 🖥️ Share sheet
| | visible
| | |
| ... user interacts with share screen ... |
| | |
| | 👆 𝗨𝘀𝘀𝗲𝗿 𝗰𝗼𝗺𝗽𝗹𝗲𝘁𝗲𝘀 𝘀𝗵𝗮𝗿𝗲,
| | 𝗻𝗮𝘃𝗶𝗴𝗮𝘁𝗲𝘀 𝘁𝗼 𝗺𝗮𝗶𝗻 𝗮𝗽𝗽
| | |
T+10000ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝘁𝗿𝘂𝗲 (init done during share) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟭𝟬𝟬𝟬𝟬 |
┃ | | |
T+10000ms┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+10050ms┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
┃ | | |
T+13500ms┃ | ├─ Content renders |
┃ | | |
┃ | | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 10000ms ║
║ TTI = [view visible time] - 10000ms ║
╚══════════════════════════════════════════════════════════════════════╝
9g: Long-press → Incoming Call - RARE
9g (COLD): Long-press → Incoming Call (COLD)
Description: User long-presses → Process starts → Incoming call arrives → MainActivity opens first → User answers call.
Result: COLD START ✅
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗹𝗼𝗻𝗴 𝗽𝗿𝗲𝘀𝘀𝗲𝘀 𝗶𝗰𝗼𝗻
| | (Samsung: process starts here;
| | Pixel: process doesn't start)
| | |
T+0ms ┿━━━ Process starts | |
| (Samsung only; Pixel: | |
| starts later when | |
| call arrives) | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| | |
| | 📞 𝗜𝗻𝗰𝗼𝗺𝗶𝗻𝗴 𝗰𝗮𝗹𝗹
| | |
T+120ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝗳𝗮𝗹𝘀𝗲 (init still running!) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟭𝟮𝟬 |
┃ | | |
T+120ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+160ms ┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
| | |
| (IncomingCallActivity may | |
| appear as overlay/system | |
| UI, not tracked in TTI) | |
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗮𝗻𝘀𝘄𝗲𝗿𝘀 𝗰𝗮𝗹𝗹
| | |
T+3500ms ┃ └─ initialize() complete | |
┃ ├─ Content renders |
┃ | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appOpenedTime = 120ms ║
║ appStartType = COLD (initialize not complete when MA opened) ║
║ TTI = [view visible time] - 120ms ║
╚══════════════════════════════════════════════════════════════════════╝
9h (WARM): Long-press → Incoming Call (WARM)
Description: User long-presses → Process starts → User waits → Incoming call arrives after initialize() completes → MainActivity opens first.
Result: WARM START ✅
Visual Diagram:
Process (NEW) MainActivity User
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗹𝗼𝗻𝗴 𝗽𝗿𝗲𝘀𝘀𝗲𝘀 𝗶𝗰𝗼𝗻
| | (Samsung: process starts here;
| | Pixel: process doesn't start)
| | |
T+0ms ┿━━━ Process starts | |
| (Samsung only; Pixel: | |
| starts later when | |
| call arrives) | |
| | |
T+50ms ├─ initialize() (bg thread) | |
| | |
T+100ms | | 🖥️ Shortcuts menu appears
| | |
| | ⏱️ 𝗨𝘀𝗲𝗿 𝘄𝗮𝗶𝘁𝘀
| | |
T+3500ms └─ initialize() complete | |
| | |
| | 📞 𝗜𝗻𝗰𝗼𝗺𝗶𝗻𝗴 𝗰𝗮𝗹𝗹
| | |
T+4000ms 🟢 ├─ instantiateActivity() | |
┃ | ├─ endsWith(".MainActivity") = TRUE |
┃ | ├─ markActivityInstantiation() |
┃ | ├─ 𝘄𝗮𝘀𝗜𝗻𝗶𝘁𝗶𝗮𝗹𝗶𝘇𝗲𝗖𝗼𝗺𝗽𝗹𝗲𝘁𝗲=𝘁𝗿𝘂𝗲 (init finished) |
┃ | ├─ staticMainActivityCreationTime == null = TRUE |
┃ | └─ 𝘀𝘁𝗮𝘁𝗶𝗰𝗠𝗮𝗶𝗻𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆=𝟰𝟬𝟬𝟬 |
┃ | | |
T+4000ms ┃ | ● 🖥️ Splash screen
┃ | | visible
┃ | | |
T+4050ms┃ | onCreate() |
┃ | | |
┃ | ├─ Inflate views |
┃ | ├─ Init React |
| | |
| (IncomingCallActivity may | |
| appear as overlay/system | |
| UI, not tracked in TTI) | |
| | |
| | 👆 𝗨𝘀𝗲𝗿 𝗮𝗻𝘀𝘄𝗲𝗿𝘀 𝗰𝗮𝗹𝗹
| | |
T+4100ms┃ | ├─ Content renders |
┃ | | |
┃ | | |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🔴 [𝗧𝗧𝗜 𝗘𝗡𝗗]
╔══════════════════════════════════════════════════════════════════════╗
║ TTI MEASUREMENT RANGE (🟢 start → 🔴 end) ║
║ appStartType = WARM (initialize complete when MainActivity opened) ║
║ appOpenedTime = 4000ms ║
║ TTI = [view visible time] - 4000ms ║
║ IncomingCallActivity (overlay/system UI) correctly EXCLUDED ║
╚══════════════════════════════════════════════════════════════════════╝
Legend:
🟢 = TTI measurement START (appOpenedTime)
🔴 = TTI measurement END (calculated in JS)
👆 = User tap/interaction (triggers action)
🖥️ = User sees UI (splash, main app, etc.)
📱 = System event (notification arrives)
📞 = Incoming call
🔄 = Configuration change (rotation)
● = MainActivity instance
┃ = TTI measurement in progress
𝗕𝗼𝗹𝗱 = Values used in TTI calculation for this scenario
Normal = Events that occur (helpful context)
API < 28 Support:
- Manual tracking in
onStartCommand()andonReceive()with API version checks - Fallback in
MainActivity.onCreate()for timestamp capture - All scenarios work identically on older Android versions
ios part lgtm