Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
bidetofevil committed Nov 8, 2024
1 parent dbd1f5f commit 716e5b7
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 97 deletions.
21 changes: 0 additions & 21 deletions embrace-android-api/api/embrace-android-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -118,27 +118,6 @@ public abstract interface class io/embrace/android/embracesdk/internal/api/UserA
public abstract fun setUsername (Ljava/lang/String;)V
}

public abstract interface class io/embrace/android/embracesdk/internal/capture/activity/OpenEvents {
public abstract fun create (ILjava/lang/String;J)V
public abstract fun createEnd (IJ)V
public abstract fun hibernate (ILjava/lang/String;J)V
public abstract fun render (ILjava/lang/String;J)V
public abstract fun renderEnd (IJ)V
public abstract fun resetTrace (ILjava/lang/String;J)V
public abstract fun resume (ILjava/lang/String;J)V
public abstract fun resumeEnd (IJ)V
public abstract fun start (ILjava/lang/String;J)V
public abstract fun startEnd (IJ)V
}

public final class io/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType : java/lang/Enum {
public static final field COLD Lio/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType;
public static final field HOT Lio/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType;
public final fun getTypeName ()Ljava/lang/String;
public static fun valueOf (Ljava/lang/String;)Lio/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType;
public static fun values ()[Lio/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType;
}

public class io/embrace/android/embracesdk/internal/network/http/EmbraceHttpPathOverride {
protected static final field PATH_OVERRIDE Ljava/lang/String;
public fun <init> ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,56 @@ package io.embrace.android.embracesdk.internal.capture.activity
/**
* The relevant stages in the lifecycle of Activities pertaining to observing the performance of their loading
*/
public interface OpenEvents {
interface OpenEvents {

/**
* When a previously in-progress Activity Open trace should be abandoned, and that the component managing
* the trace recording should prepare itself to start tracing the opening of a new Activity instance.
*/
public fun resetTrace(instanceId: Int, activityName: String, timestampMs: Long)
fun resetTrace(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the app is no longer in a state where it is trying to open up a new Activity
*/
public fun hibernate(instanceId: Int, activityName: String, timestampMs: Long)
fun hibernate(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity is entering the CREATE stage of its lifecycle.
*/
public fun create(instanceId: Int, activityName: String, timestampMs: Long)
fun create(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity has exited the CREATE stage of its lifecycle.
*/
public fun createEnd(instanceId: Int, timestampMs: Long)
fun createEnd(instanceId: Int, timestampMs: Long)

/**
* When the given Activity is entering the START stage of its lifecycle.
*/
public fun start(instanceId: Int, activityName: String, timestampMs: Long)
fun start(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity has exited the START stage of its lifecycle.
*/
public fun startEnd(instanceId: Int, timestampMs: Long)
fun startEnd(instanceId: Int, timestampMs: Long)

/**
* When the given Activity is entering the RESUME stage of its lifecycle.
*/
public fun resume(instanceId: Int, activityName: String, timestampMs: Long)
fun resume(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity has exited the RESUME stage of its lifecycle.
*/
public fun resumeEnd(instanceId: Int, timestampMs: Long)
fun resumeEnd(instanceId: Int, timestampMs: Long)

/**
* When the given Activity's first UI frame starts to be rendered.
*/
public fun render(instanceId: Int, activityName: String, timestampMs: Long)
fun render(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity's first UI frame has been displayed.
*/
public fun renderEnd(instanceId: Int, timestampMs: Long)

public enum class OpenType(public val typeName: String) {
COLD("cold"), HOT("hot")
}
fun renderEnd(instanceId: Int, timestampMs: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,49 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicReference

/**
* Observes Activity lifecycle and rendering events to create traces that model the workflow for showing an Activity on screen.
* Observes Activity lifecycle and rendering events to create traces that model the workflow for showing an Activity on screen after
* app startup has completed. This creates traces for both types of Activity opening specified in [OpenType].
*
* Depending on the version of Android and the state of the app, the start, end, and intermediate stages of the workflow will use
* timestamps from different events, which affects to preciseness of the measurement.
* timestamps from different events, which affects the precision of the measurement.
*
* Trace Start:
* The start for [OpenType.COLD]:
*
* - On Android 10+, when [ActivityLifecycleCallbacks.onActivityPostPaused] is fired, denoting that the previous activity has completed
* its [ActivityLifecycleCallbacks.onActivityPaused] callbacks and a new Activity is ready to be created.
*
* - Android 9 and lower, when [ActivityLifecycleCallbacks.onActivityPaused] is fired, denoting that the previous activity is in the
* process of exiting. This will possibly result in some cleanup work of exiting the previous activity being included in the duration
* of the next trace that is logged.
*
* Trace End:
* The start for [OpenType.HOT]
*
* - On Android 10+, when [ActivityLifecycleCallbacks.onActivityPreStarted] is fired, denoting that an existing Activity instance is ready
* to be started
*
* - Android 9 and lower, when [ActivityLifecycleCallbacks.onActivityStarted] is fired, denoting that an existing activity is in the
* process of starting. This will possibly result in some of the work to start the activity already having happened depending on the
* other callbacks that have been registered.
*
* The end for both [OpenType.COLD] and [OpenType.HOT]:
*
* - Android 10+, when the Activity's first UI frame finishes rendering and is delivered to the screen
* - Android 9 and lower.... TODO
*
* - Android 9 and lower, when [ActivityLifecycleCallbacks.onActivityResumed] is fired.
*/
public class OpenTraceEmitter(
class OpenTraceEmitter(
private val spanService: SpanService,
private val versionChecker: VersionChecker,
) : OpenEvents {

private val activeTraces: MutableMap<Int, ActivityOpenTrace> = ConcurrentHashMap()
private val traceIds: MutableMap<String, Int> = ConcurrentHashMap()
private val traceZygoteHolder: AtomicReference<OpenTraceZygote> = AtomicReference(INITIAL)
private var currentTracedInstanceId: Int? = null

override fun resetTrace(instanceId: Int, activityName: String, timestampMs: Long) {
traceIds[activityName]?.let { existingTraceId ->
if (instanceId != existingTraceId) {
endTrace(instanceId = existingTraceId, timestampMs = timestampMs, errorCode = ErrorCode.USER_ABANDON)
currentTracedInstanceId?.let { currentlyTracedInstanceId ->
if (instanceId != currentlyTracedInstanceId) {
endTrace(instanceId = currentlyTracedInstanceId, timestampMs = timestampMs, errorCode = ErrorCode.USER_ABANDON)
}
}
traceZygoteHolder.set(
Expand All @@ -60,7 +73,7 @@ public class OpenTraceEmitter(

override fun create(instanceId: Int, activityName: String, timestampMs: Long) {
startTrace(
openType = OpenEvents.OpenType.COLD,
openType = OpenType.COLD,
instanceId = instanceId,
activityName = activityName,
timestampMs = timestampMs
Expand All @@ -82,7 +95,7 @@ public class OpenTraceEmitter(

override fun start(instanceId: Int, activityName: String, timestampMs: Long) {
startTrace(
openType = OpenEvents.OpenType.HOT,
openType = OpenType.HOT,
instanceId = instanceId,
activityName = activityName,
timestampMs = timestampMs
Expand Down Expand Up @@ -147,7 +160,7 @@ public class OpenTraceEmitter(
}

private fun startTrace(
openType: OpenEvents.OpenType,
openType: OpenType,
instanceId: Int,
activityName: String,
timestampMs: Long
Expand Down Expand Up @@ -215,16 +228,16 @@ public class OpenTraceEmitter(

private fun traceName(
activityName: String,
openType: OpenEvents.OpenType
openType: OpenType
): String = "$activityName-${openType.typeName}-open"

public enum class LifecycleEvent(private val typeName: String) {
enum class LifecycleEvent(private val typeName: String) {
CREATE("create"),
START("start"),
RESUME("resume"),
RENDER("render");

public fun spanName(activityName: String): String = "$activityName-$typeName"
fun spanName(activityName: String): String = "$activityName-$typeName"
}

private data class ActivityOpenTrace(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.embrace.android.embracesdk.internal.capture.activity

/**
* The type of activity opening being traced
*/
enum class OpenType(val typeName: String) {
/**
* Activity opening where the instance has to be created
*/
COLD("cold"),

/**
* Activity opening where the instance has already been created and just needs to be started and resumed (e.g. app foregrounding)
*/
HOT("hot")
}
Loading

0 comments on commit 716e5b7

Please sign in to comment.