Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ class GestureHandlerOrchestrator(
var minimumAlphaForTraversal = DEFAULT_MIN_ALPHA_FOR_TRAVERSAL
private val gestureHandlers = arrayListOf<GestureHandler>()
private val awaitingHandlers = arrayListOf<GestureHandler>()
private val preparedHandlers = arrayListOf<GestureHandler>()

Comment thread
kosmydel marked this conversation as resolved.
// Pool of reusable lists for snapshotting `gestureHandlers` during event delivery.
private val handlerListPool = ArrayDeque<ArrayList<GestureHandler>>()

private fun obtainHandlerList() = handlerListPool.pollLast() ?: ArrayList<GestureHandler>()

private fun recycleHandlerList(list: ArrayList<GestureHandler>) {
list.clear()
handlerListPool.addLast(list)
}

// In `onHandlerStateChange` method we iterate through `awaitingHandlers`, but calling `tryActivate` may modify this list.
// To avoid `ConcurrentModificationException` we iterate through copy. There is one more problem though - if handler was
Expand Down Expand Up @@ -260,20 +269,24 @@ class GestureHandlerOrchestrator(
}

private fun deliverEventToGestureHandlers(event: MotionEvent) {
// Copy handlers to "prepared handlers" array, because the list of active handlers can change
// as a result of state updates
preparedHandlers.clear()
preparedHandlers.addAll(gestureHandlers)
// Snapshot handlers into a pooled list, because the list of active handlers can change
// as a result of state updates (and delivery can be re-entrant).
val handlersToProcess = obtainHandlerList()
handlersToProcess.addAll(gestureHandlers)

Comment thread
kosmydel marked this conversation as resolved.
// We want to deliver events to active handlers first in order of their activation (handlers
// that activated first will first get event delivered). Otherwise we deliver events in the
// order in which handlers has been added ("most direct" children goes first). Therefore we rely
// on Arrays.sort providing a stable sort (as children are registered in order in which they
// should be tested)
preparedHandlers.sortWith(handlersComparator)
handlersToProcess.sortWith(handlersComparator)

for (handler in preparedHandlers) {
deliverEventToGestureHandler(handler, event)
try {
for (handler in handlersToProcess) {
deliverEventToGestureHandler(handler, event)
}
} finally {
recycleHandlerList(handlersToProcess)
}
}

Expand All @@ -284,12 +297,9 @@ class GestureHandlerOrchestrator(
handler.cancel()
}

// Copy handlers to "prepared handlers" array, because the list of active handlers can change
// Iterate over a copy, because the list of active handlers can change
// as a result of state updates
preparedHandlers.clear()
preparedHandlers.addAll(gestureHandlers)

for (handler in gestureHandlers.asReversed()) {
for (handler in gestureHandlers.reversed()) {
handler.cancel()
}
}
Expand Down
Loading