Skip to content

Commit

Permalink
Use onCreate invocation to denote Activity init start time
Browse files Browse the repository at this point in the history
  • Loading branch information
bidetofevil committed May 12, 2024
1 parent 4ad54a8 commit e31760d
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import android.os.Build.VERSION_CODES
import android.os.Process
import io.embrace.android.embracesdk.internal.clock.nanosToMillis
import io.embrace.android.embracesdk.internal.spans.SpanService
import io.embrace.android.embracesdk.internal.spans.toEmbraceAttributeName
import io.embrace.android.embracesdk.internal.utils.Provider
import io.embrace.android.embracesdk.internal.utils.VersionChecker
import io.embrace.android.embracesdk.logging.EmbLogger
import io.embrace.android.embracesdk.spans.EmbraceSpan
import io.embrace.android.embracesdk.spans.PersistableEmbraceSpan
import io.embrace.android.embracesdk.worker.BackgroundWorker
import io.opentelemetry.sdk.common.Clock
import java.util.concurrent.ConcurrentLinkedQueue
Expand Down Expand Up @@ -66,9 +66,18 @@ internal class AppStartupTraceEmitter(
@Volatile
private var applicationInitEndMs: Long? = null

@Volatile
private var startupActivityName: String? = null

@Volatile
private var startupActivityPreCreatedMs: Long? = null

@Volatile
private var startupActivityInitStartMs: Long? = null

@Volatile
private var startupActivityPostCreatedMs: Long? = null

@Volatile
private var startupActivityInitEndMs: Long? = null

Expand All @@ -89,24 +98,32 @@ internal class AppStartupTraceEmitter(
applicationInitEndMs = timestampMs ?: nowMs()
}

fun startupActivityPreCreated(timestampMs: Long? = null) {
startupActivityPreCreatedMs = timestampMs ?: nowMs()
}

fun startupActivityInitStart(timestampMs: Long? = null) {
if (startupActivityInitStartMs == null) {
startupActivityInitStartMs = timestampMs ?: nowMs()
}
startupActivityInitStartMs = timestampMs ?: nowMs()
}

fun startupActivityPostCreated(timestampMs: Long? = null) {
startupActivityPostCreatedMs = timestampMs ?: nowMs()
}

fun startupActivityInitEnd(timestampMs: Long? = null) {
startupActivityInitEndMs = timestampMs ?: nowMs()
}

fun startupActivityResumed(timestampMs: Long? = null) {
fun startupActivityResumed(activityName: String, timestampMs: Long? = null) {
startupActivityName = activityName
startupActivityResumedMs = timestampMs ?: nowMs()
if (!endWithFrameDraw) {
dataCollectionComplete()
}
}

fun firstFrameRendered(timestampMs: Long? = null) {
fun firstFrameRendered(activityName: String, timestampMs: Long? = null) {
startupActivityName = activityName
firstFrameRenderedMs = timestampMs ?: nowMs()
if (endWithFrameDraw) {
dataCollectionComplete()
Expand Down Expand Up @@ -169,17 +186,15 @@ internal class AppStartupTraceEmitter(
activityInitStartMs = startupActivityInitStartMs,
activityInitEndMs = startupActivityInitEndMs,
traceEndTimeMs = traceEndTimeMs,
processCreateDelay = processCreateDelay(),
)
} else {
startupActivityInitStartMs?.let { startTime ->
recordWarmTtid(
traceStartTimeMs = startTime,
activityInitEndMs = startupActivityInitEndMs,
traceEndTimeMs = traceEndTimeMs,
processCreateDelay = processCreateDelay(),
processToActivityCreateGap = gap,
sdkStartupDuration = sdkStartupDuration
sdkStartupDuration = sdkStartupDuration,
)
}
}
Expand Down Expand Up @@ -214,17 +229,15 @@ internal class AppStartupTraceEmitter(
activityInitStartMs: Long?,
activityInitEndMs: Long?,
traceEndTimeMs: Long,
processCreateDelay: Long?,
): EmbraceSpan? {
return if (!startupRecorded.get()) {
spanService.startSpan(
name = "cold-time-to-initial-display",
startTimeMs = traceStartTimeMs,
private = false
)?.apply {
processCreateDelay?.let { delay ->
addAttribute("process-create-delay-ms".toEmbraceAttributeName(), delay.toString())
}
addTraceMetadata()

if (stop(endTimeMs = traceEndTimeMs)) {
startupRecorded.set(true)
}
Expand Down Expand Up @@ -290,7 +303,6 @@ internal class AppStartupTraceEmitter(
traceStartTimeMs: Long,
activityInitEndMs: Long?,
traceEndTimeMs: Long,
processCreateDelay: Long?,
processToActivityCreateGap: Long?,
sdkStartupDuration: Long?,
): EmbraceSpan? {
Expand All @@ -300,15 +312,15 @@ internal class AppStartupTraceEmitter(
startTimeMs = traceStartTimeMs,
private = false,
)?.apply {
processCreateDelay?.let { delay ->
addAttribute("process-create-delay-ms".toEmbraceAttributeName(), delay.toString())
}
processToActivityCreateGap?.let { gap ->
addAttribute("activity-init-gap-ms".toEmbraceAttributeName(), gap.toString())
addAttribute("activity-init-gap-ms", gap.toString())
}
sdkStartupDuration?.let { duration ->
addAttribute("embrace-init-duration-ms".toEmbraceAttributeName(), duration.toString())
addAttribute("embrace-init-duration-ms", duration.toString())
}

addTraceMetadata()

if (stop(endTimeMs = traceEndTimeMs)) {
startupRecorded.set(true)
}
Expand Down Expand Up @@ -345,6 +357,24 @@ internal class AppStartupTraceEmitter(

private fun nowMs(): Long = clock.now().nanosToMillis()

private fun PersistableEmbraceSpan.addTraceMetadata() {
processCreateDelay()?.let { delay ->
addAttribute("process-create-delay-ms", delay.toString())
}

startupActivityName?.let { name ->
addAttribute("startup-activity-name", name)
}

startupActivityPreCreatedMs?.let { timeMs ->
addAttribute("startup-activity-pre-created-ms", timeMs.toString())
}

startupActivityPostCreatedMs?.let { timeMs ->
addAttribute("startup-activity-post-created-ms", timeMs.toString())
}
}

private data class TrackedInterval(
val name: String,
val startTimeMs: Long,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,17 @@ internal class StartupTracker(
) : Application.ActivityLifecycleCallbacks {
private var isFirstDraw = false
private var nullWindowCallbackErrorLogged = false
private var startupActivityId: Int? = null

override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
if (activity.useAsStartupActivity()) {
appStartupTraceEmitter.startupActivityPreCreated()
}
}

override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
if (activity.observeForStartup()) {
if (activity.useAsStartupActivity()) {
val activityName = activity.localClassName
appStartupTraceEmitter.startupActivityInitStart()
if (versionChecker.isAtLeast(Build.VERSION_CODES.Q)) {
if (!isFirstDraw) {
Expand All @@ -55,7 +63,7 @@ internal class StartupTracker(
decorView.onNextDraw {
if (!isFirstDraw) {
isFirstDraw = true
val callback = { appStartupTraceEmitter.firstFrameRendered() }
val callback = { appStartupTraceEmitter.firstFrameRendered(activityName = activityName) }
decorView.viewTreeObserver.registerFrameCommitCallback(callback)
}
}
Expand All @@ -69,21 +77,21 @@ internal class StartupTracker(
}
}

override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
if (activity.observeForStartup()) {
appStartupTraceEmitter.startupActivityInitStart()
override fun onActivityPostCreated(activity: Activity, savedInstanceState: Bundle?) {
if (activity.useAsStartupActivity()) {
appStartupTraceEmitter.startupActivityPostCreated()
}
}

override fun onActivityStarted(activity: Activity) {
if (activity.observeForStartup()) {
if (activity.isStartupActivity()) {
appStartupTraceEmitter.startupActivityInitEnd()
}
}

override fun onActivityResumed(activity: Activity) {
if (activity.observeForStartup()) {
appStartupTraceEmitter.startupActivityResumed()
appStartupTraceEmitter.startupActivityResumed(activityName = activity.localClassName)
}
}

Expand All @@ -95,6 +103,34 @@ internal class StartupTracker(

override fun onActivityDestroyed(activity: Activity) {}

/**
* Returns true if the Activity instance is being used as the startup Activity. It will return false if [useAsStartupActivity] has
* not been called previously to setup the Activity instance to be used as the startup Activity.
*/
private fun Activity.isStartupActivity(): Boolean {
return if (observeForStartup()) {
startupActivityId == hashCode()
} else {
false
}
}

/**
* Use this Activity instance as the startup activity if appropriate. Return true the current instance is the startup Activity
* instance going forward, false otherwise.
*/
private fun Activity.useAsStartupActivity(): Boolean {
if (isStartupActivity()) {
return true
}

if (observeForStartup()) {
startupActivityId = hashCode()
}

return isStartupActivity()
}

companion object {
private class PyNextDrawListener(
val view: View,
Expand Down

0 comments on commit e31760d

Please sign in to comment.