-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interaction latency rules #38
Conversation
@@ -36,5 +39,35 @@ interface InputTracker { | |||
|
|||
class DeliveredInput<T : InputEvent>( | |||
val event: T, | |||
val deliveryUptimeMillis: Long | |||
) | |||
val deliveryUptime: Duration, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these years I had no idea we had a Duration class in Kotlin (and also another one in Java!!)
import kotlin.time.Duration | ||
import kotlin.time.Duration.Companion.milliseconds | ||
import kotlin.time.DurationUnit.MILLISECONDS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@0legg Should PY be using 310 alternatives here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really taking a liking to Kotlin's duration class, looks nicer than the JDK ones.
val stateHolder = PerformanceMetricsState.getForHierarchy(textView).state!! | ||
stateHolder.addState("textview", "updated at $date") | ||
interactionEventReceiver.sendEvent(OnMainActivityButtonClick(element, previousText, newText)) | ||
// val stateHolder = PerformanceMetricsState.getForHierarchy(textView).state!! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Remove commented code?
|
||
override val currentKeyEvent: DeliveredInput<KeyEvent>? | ||
get() = currentKeyEventLocal.get() | ||
|
||
private val motionEventTriggeringClickLocal = ThreadLocal<DeliveredInput<MotionEvent>>() | ||
/** | ||
* The thread locals here aren't in charge of actually holding the event holder for the duration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This would be easier to read if it were broken up into multiple sentences.
* The thread locals here aren't in charge of actually holding the event holder for the duration | ||
* of its lifecycle, but instead of holding the event in specific time spans (wrapping onClick | ||
* callback invocations) so that we can reach back here from an onClick and capture the event but | ||
* also ensure that if we reach back outside of an onClick sandwich we actually find nothing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: find nothing event if overall
-> Seems like it's missing a word or two in there?
// if this event is consumed as part of the frame we did the increment in. | ||
// There's a slight edge case: if the event consumption triggered in between doFrame and | ||
// the post at front of queue, the count would be short by 1. We can live with this, it's | ||
// unlikely to happen unless and even is triggered from a postAtFront. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: and even
-> an event
?
// holder. Why replace it? Because we want to increase the frame count over time, but we | ||
// want to do that by swapping an immutable event, so that if we capture such event at | ||
// time N and then the count gets updated at N + 1, the count update isn't reflected in | ||
// the code that captured the event a time N. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: a time
-> at time
val event: InputEventType, | ||
val deliveryUptime: Duration, | ||
val framesSinceDelivery: Int, | ||
private var endTrace: ((() -> Unit))? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Is there an unnecessary set of parentheses there?
) { | ||
|
||
val eventUptime: Duration | ||
get() = event.eventTime.milliseconds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Does this need to be a custom getter, or can it just be initialized? Can event.eventTime.milliseconds
change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It won't change, but as a general practice I avoid creating backing fields when delegation works just fine and is cheap, especially if the backing field might never be actually read.
private val onCancel: (CanceledInteractionResult<InteractionType>) -> Unit | ||
) : RunningInteraction<InteractionType>, FinishingInteraction<InteractionType>, FrameCallback { | ||
|
||
private var frameCount: Int = if (isChoreographerDoingFrame()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: One line without braces?
override fun sendEvent(event: EventType) { | ||
val eventSentUptime = System.nanoTime().nanoseconds | ||
if (isMainThread) { | ||
for (engine in interactionEngines) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: interactionEngines.forEach
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find traditional for loops a bit more readable but to be honest it's not like a strongly informed opinion.
} else { | ||
mainHandler.post { | ||
for (engine in interactionEngines) { | ||
engine.sendEvent(event, eventSentUptime, null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the interactionInput
parameter null?
Redesigning the API and implementation for tracking interaction latency, with a primary goal of decoupling the code where we send signals (events) from the place where we tie those events together an interaction.
Example usage code