-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
74a2e3d
commit a3f657c
Showing
10 changed files
with
878 additions
and
14 deletions.
There are no files selected for viewing
109 changes: 109 additions & 0 deletions
109
...src/main/java/io/embrace/android/embracesdk/internal/capture/activity/LoadEventEmitter.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package io.embrace.android.embracesdk.internal.capture.activity | ||
|
||
import android.app.Activity | ||
import android.os.Build | ||
import android.os.Bundle | ||
import io.embrace.android.embracesdk.internal.clock.nanosToMillis | ||
import io.embrace.android.embracesdk.internal.session.lifecycle.ActivityLifecycleListener | ||
import io.embrace.android.embracesdk.internal.utils.VersionChecker | ||
import io.opentelemetry.sdk.common.Clock | ||
|
||
internal class LoadEventEmitter( | ||
private val openEvents: LoadEvents, | ||
private val clock: Clock, | ||
private val versionChecker: VersionChecker, | ||
): ActivityLifecycleListener { | ||
override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) { | ||
create(activity) | ||
} | ||
|
||
override fun onActivityCreated(activity: Activity, bundle: Bundle?) { | ||
if (!versionChecker.hasPrePostEvents()) { | ||
create(activity) | ||
} | ||
} | ||
|
||
override fun onActivityPostCreated(activity: Activity, savedInstanceState: Bundle?) { | ||
createEnd(activity) | ||
} | ||
|
||
override fun onActivityPreStarted(activity: Activity) { | ||
start(activity) | ||
} | ||
|
||
override fun onActivityStarted(activity: Activity) { | ||
if (!versionChecker.hasPrePostEvents()) { | ||
createEnd(activity) | ||
start(activity) | ||
} | ||
} | ||
|
||
override fun onActivityPostStarted(activity: Activity) { | ||
startEnd(activity) | ||
} | ||
|
||
override fun onActivityPreResumed(activity: Activity) { | ||
resume(activity) | ||
} | ||
|
||
override fun onActivityResumed(activity: Activity) { | ||
if (!versionChecker.hasPrePostEvents()) { | ||
startEnd(activity) | ||
resumeEnd(activity) | ||
} | ||
} | ||
|
||
override fun onActivityPostResumed(activity: Activity) { | ||
resumeEnd(activity) | ||
} | ||
|
||
private fun create(activity: Activity) { | ||
openEvents.create( | ||
instanceId = traceInstanceId(activity), | ||
activityName = activity.localClassName, | ||
timestampMs = nowMs() | ||
) | ||
} | ||
|
||
private fun createEnd(activity: Activity) { | ||
openEvents.createEnd( | ||
instanceId = traceInstanceId(activity), | ||
timestampMs = nowMs() | ||
) | ||
} | ||
|
||
private fun start(activity: Activity) { | ||
openEvents.start( | ||
instanceId = traceInstanceId(activity), | ||
activityName = activity.localClassName, | ||
timestampMs = nowMs() | ||
) | ||
} | ||
|
||
private fun startEnd(activity: Activity) { | ||
openEvents.startEnd( | ||
instanceId = traceInstanceId(activity), | ||
timestampMs = nowMs() | ||
) | ||
} | ||
|
||
private fun resume(activity: Activity) { | ||
openEvents.resume( | ||
instanceId = traceInstanceId(activity), | ||
timestampMs = nowMs() | ||
) | ||
} | ||
|
||
private fun resumeEnd(activity: Activity) { | ||
openEvents.resumeEnd( | ||
instanceId = traceInstanceId(activity), | ||
timestampMs = nowMs() | ||
) | ||
} | ||
|
||
private fun VersionChecker.hasPrePostEvents(): Boolean = isAtLeast(Build.VERSION_CODES.Q) | ||
|
||
private fun traceInstanceId(activity: Activity): Int = activity.hashCode() | ||
|
||
private fun nowMs(): Long = clock.now().nanosToMillis() | ||
} |
28 changes: 28 additions & 0 deletions
28
...d-sdk/src/main/java/io/embrace/android/embracesdk/internal/capture/activity/LoadEvents.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package io.embrace.android.embracesdk.internal.capture.activity | ||
|
||
internal interface LoadEvents { | ||
|
||
fun create(instanceId: Int, activityName: String, timestampMs: Long) | ||
|
||
fun createEnd(instanceId: Int, timestampMs: Long) | ||
|
||
fun start(instanceId: Int, activityName: String, timestampMs: Long) | ||
|
||
fun startEnd(instanceId: Int, timestampMs: Long) | ||
|
||
fun resume(instanceId: Int, timestampMs: Long) | ||
|
||
fun resumeEnd(instanceId: Int, timestampMs: Long) | ||
|
||
fun firstRender(instanceId: Int, timestampMs: Long) | ||
|
||
fun firstRenderEnd(instanceId: Int, timestampMs: Long) | ||
|
||
enum class OpenType(val typeName: String) { | ||
COLD("cold"), HOT("hot") | ||
} | ||
|
||
enum class EndEvent(val eventName: String) { | ||
RENDER("render"), RESUME("open") | ||
} | ||
} |
182 changes: 182 additions & 0 deletions
182
...src/main/java/io/embrace/android/embracesdk/internal/capture/activity/LoadTraceEmitter.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
package io.embrace.android.embracesdk.internal.capture.activity | ||
|
||
import android.os.Build | ||
import io.embrace.android.embracesdk.internal.spans.PersistableEmbraceSpan | ||
import io.embrace.android.embracesdk.internal.spans.SpanService | ||
import io.embrace.android.embracesdk.internal.utils.VersionChecker | ||
import io.embrace.android.embracesdk.internal.worker.BackgroundWorker | ||
import java.util.concurrent.ConcurrentHashMap | ||
|
||
internal class LoadTraceEmitter( | ||
private val spanService: SpanService, | ||
private val backgroundWorker: BackgroundWorker, | ||
private val versionChecker: VersionChecker, | ||
) : LoadEvents { | ||
|
||
private val activeTraces: MutableMap<Int, ActivityLoadTrace> = ConcurrentHashMap() | ||
|
||
override fun create(instanceId: Int, activityName: String, timestampMs: Long) { | ||
startTrace( | ||
openType = LoadEvents.OpenType.COLD, | ||
instanceId = instanceId, | ||
activityName = activityName, | ||
timestampMs = timestampMs | ||
) | ||
startChildSpan( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
childType = ChildType.CREATE | ||
) | ||
} | ||
|
||
override fun createEnd(instanceId: Int, timestampMs: Long) { | ||
endChildSpan( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
childType = ChildType.CREATE | ||
) | ||
} | ||
|
||
override fun start(instanceId: Int, activityName: String, timestampMs: Long) { | ||
startTrace( | ||
openType = LoadEvents.OpenType.HOT, | ||
instanceId = instanceId, | ||
activityName = activityName, | ||
timestampMs = timestampMs | ||
) | ||
startChildSpan( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
childType = ChildType.START | ||
) | ||
} | ||
|
||
override fun startEnd(instanceId: Int, timestampMs: Long) { | ||
endChildSpan( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
childType = ChildType.START | ||
) | ||
} | ||
|
||
override fun resume(instanceId: Int, timestampMs: Long) { | ||
startChildSpan( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
childType = ChildType.RESUME | ||
) | ||
} | ||
|
||
override fun resumeEnd(instanceId: Int, timestampMs: Long) { | ||
endChildSpan( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
childType = ChildType.RESUME | ||
) | ||
|
||
if (getEndEvent() == LoadEvents.EndEvent.RESUME) { | ||
endTrace( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
) | ||
} | ||
} | ||
|
||
override fun firstRender(instanceId: Int, timestampMs: Long) { | ||
startChildSpan( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
childType = ChildType.RENDER | ||
) | ||
} | ||
|
||
override fun firstRenderEnd(instanceId: Int, timestampMs: Long) { | ||
endChildSpan( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
childType = ChildType.RENDER | ||
) | ||
endTrace( | ||
instanceId = instanceId, | ||
timestampMs = timestampMs, | ||
) | ||
} | ||
|
||
private fun startTrace( | ||
openType: LoadEvents.OpenType, | ||
instanceId: Int, | ||
activityName: String, | ||
timestampMs: Long | ||
) { | ||
if (!activeTraces.containsKey(instanceId)) { | ||
spanService.startSpan( | ||
name = traceName(activityName, openType), | ||
startTimeMs = timestampMs, | ||
)?.let { root -> | ||
activeTraces[instanceId] = ActivityLoadTrace(root = root, activityName = activityName) | ||
} | ||
} | ||
} | ||
|
||
private fun endTrace(instanceId: Int, timestampMs: Long) { | ||
activeTraces[instanceId]?.let { trace -> | ||
backgroundWorker.submit { | ||
trace.root.stop(timestampMs) | ||
activeTraces.remove(instanceId) | ||
} | ||
} | ||
} | ||
|
||
private fun startChildSpan(instanceId: Int, timestampMs: Long, childType: ChildType) { | ||
val trace = activeTraces[instanceId] | ||
if (trace != null && !trace.children.containsKey(childType)) { | ||
backgroundWorker.submit { | ||
spanService.startSpan( | ||
name = childType.spanName(trace.activityName), | ||
parent = trace.root, | ||
startTimeMs = timestampMs, | ||
)?.let { newSpan -> | ||
activeTraces[instanceId] = trace.copy( | ||
children = trace.children.plus(childType to newSpan) | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun endChildSpan(instanceId: Int, timestampMs: Long, childType: ChildType) { | ||
activeTraces[instanceId]?.let { trace -> | ||
backgroundWorker.submit { | ||
trace.children[childType]?.stop(timestampMs) | ||
} | ||
} | ||
} | ||
|
||
private fun getEndEvent(): LoadEvents.EndEvent { | ||
return if (versionChecker.isAtLeast(Build.VERSION_CODES.Q)) { | ||
LoadEvents.EndEvent.RENDER | ||
} else { | ||
LoadEvents.EndEvent.RESUME | ||
} | ||
} | ||
|
||
private fun traceName( | ||
activityName: String, | ||
openType: LoadEvents.OpenType | ||
): String = "$activityName-${openType.typeName}-${getEndEvent().eventName}" | ||
|
||
private enum class ChildType(val typeName: String) { | ||
CREATE("create"), | ||
START("start"), | ||
RESUME("resume"), | ||
RENDER("render"); | ||
|
||
fun spanName(activityName: String): String = "activity-$typeName-$activityName" | ||
} | ||
|
||
private data class ActivityLoadTrace( | ||
val activityName: String, | ||
val root: PersistableEmbraceSpan, | ||
val children: Map<ChildType, PersistableEmbraceSpan> = emptyMap(), | ||
) | ||
} |
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
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
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
Oops, something went wrong.