Last active
October 7, 2021 02:12
-
-
Save maxirosson/952f5f19b7bf381be2281c5320a1d396 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.dipien.startup | |
import android.app.Activity | |
import android.app.Application | |
import android.content.Context | |
import android.os.Bundle | |
import androidx.lifecycle.Lifecycle | |
import androidx.lifecycle.LifecycleObserver | |
import androidx.lifecycle.OnLifecycleEvent | |
import androidx.lifecycle.ProcessLifecycleOwner | |
import com.google.firebase.ktx.Firebase | |
import com.google.firebase.perf.ktx.performance | |
import com.google.firebase.perf.metrics.Trace | |
import java.util.concurrent.TimeUnit | |
/** | |
* A class to capture the Android AppStart Trace information. | |
* https://github.com/firebase/firebase-android-sdk/blob/master/firebase-perf/src/main/java/com/google/firebase/perf/provider/FirebasePerfProvider.java | |
*/ | |
object StartupTrace : Application.ActivityLifecycleCallbacks, LifecycleObserver { | |
private val TAG = StartupTimeProvider::class.simpleName | |
private val MAX_LATENCY_BEFORE_UI_INIT = TimeUnit.MINUTES.toMillis(1) | |
var appStartTime: Long? = null | |
private var onCreateTime: Long? = null | |
var isStartedFromBackground = false | |
var atLeastOnTimeOnBackground = false | |
private var isRegisteredForLifecycleCallbacks = false | |
private var appContext: Context? = null | |
private var trace: Trace? = null | |
/** | |
* If the time difference between app starts and creation of any Activity is larger than | |
* MAX_LATENCY_BEFORE_UI_INIT, set isTooLateToInitUI to true and we don't send AppStart Trace. | |
*/ | |
var isTooLateToInitUI = false | |
fun onColdStartInitiated(context: Context) { | |
appStartTime = System.currentTimeMillis() | |
trace = Firebase.performance.newTrace("cold_startup_time") | |
trace!!.start() | |
val appContext = context.applicationContext | |
if (appContext is Application) { | |
appContext.registerActivityLifecycleCallbacks(this) | |
ProcessLifecycleOwner.get().lifecycle.addObserver(this) | |
isRegisteredForLifecycleCallbacks = true | |
this.appContext = appContext | |
} | |
} | |
/** Unregister this callback after AppStart trace is logged. */ | |
private fun unregisterActivityLifecycleCallbacks() { | |
if (!isRegisteredForLifecycleCallbacks) { | |
return | |
} | |
(appContext as Application).unregisterActivityLifecycleCallbacks(this) | |
isRegisteredForLifecycleCallbacks = false | |
} | |
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { | |
if (isStartedFromBackground || onCreateTime != null) { | |
return | |
} | |
onCreateTime = System.currentTimeMillis() | |
if ((onCreateTime!! - appStartTime!!) > MAX_LATENCY_BEFORE_UI_INIT) { | |
isTooLateToInitUI = true | |
} | |
} | |
override fun onActivityStarted(activity: Activity) { } | |
override fun onActivityResumed(activity: Activity) { | |
if (isStartedFromBackground || isTooLateToInitUI || atLeastOnTimeOnBackground) { | |
unregisterActivityLifecycleCallbacks() | |
return | |
} | |
if (activity !is LoadingActivity) { | |
Log.d(TAG, "Cold start finished after ${System.currentTimeMillis() - appStartTime!!}ms") | |
trace?.stop() | |
trace = null | |
if (isRegisteredForLifecycleCallbacks) { | |
// After AppStart trace is logged, we can unregister this callback. | |
unregisterActivityLifecycleCallbacks() | |
} | |
} | |
} | |
override fun onActivityPaused(activity: Activity) { } | |
override fun onActivityStopped(activity: Activity) { } | |
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { } | |
override fun onActivityDestroyed(activity: Activity) { } | |
@OnLifecycleEvent(Lifecycle.Event.ON_STOP) | |
fun onEnterBackground() { | |
atLeastOnTimeOnBackground = true | |
ProcessLifecycleOwner.get().lifecycle.removeObserver(this) | |
} | |
/** | |
* We use StartFromBackgroundRunnable to detect if app is started from background or foreground. | |
* If app is started from background, we do not generate AppStart trace. This runnable is posted | |
* to main UI thread from StartupTimeProvider. If app is started from background, this runnable | |
* will be executed before any activity's onCreate() method. If app is started from foreground, | |
* activity's onCreate() method is executed before this runnable. | |
*/ | |
object StartFromBackgroundRunnable : Runnable { | |
override fun run() { | |
// if no activity has ever been created. | |
if (onCreateTime == null) { | |
isStartedFromBackground = true | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment