Add parent id to decor view

Summary: This whole nested observer approach can be simplified massively, but for now we are just threading the parent id through so its set for observer roots too

Reviewed By: lblasa

Differential Revision: D47915504

fbshipit-source-id: 924f722f38bb202b42ea9ef6da15e685f6c75e02
This commit is contained in:
Luke De Feo
2023-07-31 10:53:03 -07:00
committed by Facebook GitHub Bot
parent 413f85964b
commit 6b74f66ab4
5 changed files with 26 additions and 17 deletions

View File

@@ -13,6 +13,7 @@ import com.facebook.flipper.plugins.uidebugger.LogTag
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver
import com.facebook.flipper.plugins.uidebugger.core.UIDContext import com.facebook.flipper.plugins.uidebugger.core.UIDContext
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
import com.facebook.flipper.plugins.uidebugger.util.objectIdentity import com.facebook.flipper.plugins.uidebugger.util.objectIdentity
/** /**
@@ -23,7 +24,7 @@ class ApplicationTreeObserver(val context: UIDContext) : TreeObserver<Applicatio
override val type = "Application" override val type = "Application"
override fun subscribe(node: Any) { override fun subscribe(node: Any, parentId: Id?) {
Log.i(LogTag, "Subscribing activity / root view changes") Log.i(LogTag, "Subscribing activity / root view changes")
val applicationRef = node as ApplicationRef val applicationRef = node as ApplicationRef
@@ -41,7 +42,7 @@ class ApplicationTreeObserver(val context: UIDContext) : TreeObserver<Applicatio
} }
context.sharedThrottle.registerCallback(this.objectIdentity()) { context.sharedThrottle.registerCallback(this.objectIdentity()) {
traverseAndSend(context, applicationRef) traverseAndSend(null, context, applicationRef)
} }
context.applicationRef.rootsResolver.attachListener(rootViewListener) context.applicationRef.rootsResolver.attachListener(rootViewListener)

View File

@@ -13,6 +13,7 @@ import android.view.ViewTreeObserver
import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.LogTag
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
import com.facebook.flipper.plugins.uidebugger.core.UIDContext import com.facebook.flipper.plugins.uidebugger.core.UIDContext
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
import com.facebook.flipper.plugins.uidebugger.util.objectIdentity import com.facebook.flipper.plugins.uidebugger.util.objectIdentity
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
@@ -26,14 +27,14 @@ class DecorViewObserver(val context: UIDContext) : TreeObserver<DecorView>() {
override val type = "DecorView" override val type = "DecorView"
override fun subscribe(node: Any) { override fun subscribe(node: Any, parentId: Id?) {
node as View node as View
nodeRef = WeakReference(node) nodeRef = WeakReference(node)
Log.i(LogTag, "Subscribing to decor view changes") Log.i(LogTag, "Subscribing to decor view changes")
context.sharedThrottle.registerCallback(this.objectIdentity()) { context.sharedThrottle.registerCallback(this.objectIdentity()) {
nodeRef?.get()?.let { traverseAndSendWithSnapshot() } nodeRef?.get()?.let { traverseAndSendWithSnapshot(parentId) }
} }
preDrawListener = preDrawListener =
@@ -46,16 +47,21 @@ class DecorViewObserver(val context: UIDContext) : TreeObserver<DecorView>() {
// It can be the case that the DecorView the current observer owns has already // It can be the case that the DecorView the current observer owns has already
// drawn. In this case, manually trigger an update. // drawn. In this case, manually trigger an update.
traverseAndSendWithSnapshot() traverseAndSendWithSnapshot(parentId)
} }
private fun traverseAndSendWithSnapshot() { private fun traverseAndSendWithSnapshot(parentId: Id?) {
nodeRef?.get()?.let { view -> nodeRef?.get()?.let { view ->
var snapshotBitmap: BitmapPool.ReusableBitmap? = null var snapshotBitmap: BitmapPool.ReusableBitmap? = null
if (view.width > 0 && view.height > 0) { if (view.width > 0 && view.height > 0) {
snapshotBitmap = context.bitmapPool.getBitmap(view.width, view.height) snapshotBitmap = context.bitmapPool.getBitmap(view.width, view.height)
} }
traverseAndSend(context, view, snapshotBitmap) traverseAndSend(
parentId,
context,
view,
snapshotBitmap,
)
} }
} }

View File

@@ -35,35 +35,37 @@ abstract class TreeObserver<T> {
abstract val type: String abstract val type: String
abstract fun subscribe(node: Any) abstract fun subscribe(node: Any, parentId: Id?)
abstract fun unsubscribe() abstract fun unsubscribe()
/** Traverses the layout hierarchy while managing any encountered child observers. */ /** Traverses the layout hierarchy while managing any encountered child observers. */
fun traverseAndSend( fun traverseAndSend(
parentId: Id?,
context: UIDContext, context: UIDContext,
root: Any, root: Any,
snapshotBitmap: BitmapPool.ReusableBitmap? = null, snapshotBitmap: BitmapPool.ReusableBitmap? = null,
frameworkEvents: List<FrameworkEvent>? = null frameworkEvents: List<FrameworkEvent>? = null
) { ) {
val traversalStartTimestamp = System.currentTimeMillis() val traversalStartTimestamp = System.currentTimeMillis()
val (visitedNodes, observableRoots) = context.layoutTraversal.traverse(root) val (visitedNodes, observableRoots) = context.layoutTraversal.traverse(root, parentId)
// Add any new observers // Add any new observers
observableRoots.forEach { observable -> observableRoots.forEach { (observable, parentId) ->
if (!children.containsKey(observable.objectIdentity())) { if (!children.containsKey(observable.objectIdentity())) {
context.observerFactory.createObserver(observable, context)?.let { observer -> context.observerFactory.createObserver(observable, context)?.let { observer ->
Log.d( Log.d(
LogTag, LogTag,
"Observer ${this.type} discovered new child of type ${observer.type} Node ID ${observable.objectIdentity()}") "Observer ${this.type} discovered new child of type ${observer.type} Node ID ${observable.objectIdentity()}")
observer.subscribe(observable) observer.subscribe(observable, parentId)
children[observable.objectIdentity()] = observer children[observable.objectIdentity()] = observer
} }
} }
} }
// Remove any old observers // Remove any old observers
val observableRootsIdentifiers = observableRoots.map { it.objectIdentity() } val observableRootsIdentifiers =
observableRoots.map { (observable, _) -> observable.objectIdentity() }
val removables = mutableListOf<Id>() val removables = mutableListOf<Id>()
children.keys.forEach { key -> children.keys.forEach { key ->
if (!observableRootsIdentifiers.contains(key)) { if (!observableRootsIdentifiers.contains(key)) {

View File

@@ -78,7 +78,7 @@ class TreeObserverManager(val context: UIDContext) {
mainScope.launch { start() } mainScope.launch { start() }
} }
batchedUpdates = Channel(Channel.UNLIMITED) batchedUpdates = Channel(Channel.UNLIMITED)
rootObserver.subscribe(context.applicationRef) rootObserver.subscribe(context.applicationRef, null)
job = job =
workerScope.launch { workerScope.launch {

View File

@@ -31,14 +31,14 @@ class PartialLayoutTraversal(
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
internal fun NodeDescriptor<*>.asAny(): NodeDescriptor<Any> = this as NodeDescriptor<Any> internal fun NodeDescriptor<*>.asAny(): NodeDescriptor<Any> = this as NodeDescriptor<Any>
fun traverse(root: Any): Pair<List<MaybeDeferred<Node>>, List<Any>> { fun traverse(root: Any, parentId: Id?): Pair<List<MaybeDeferred<Node>>, List<Pair<Any, Id?>>> {
val visited = mutableListOf<MaybeDeferred<Node>>() val visited = mutableListOf<MaybeDeferred<Node>>()
val observableRoots = mutableListOf<Any>() val observableRoots = mutableListOf<Pair<Any, Id?>>()
// cur and parent Id // cur and parent Id
val stack = mutableListOf<Pair<Any, Id?>>() val stack = mutableListOf<Pair<Any, Id?>>()
stack.add(Pair(root, null)) stack.add(Pair(root, parentId))
val shallow = mutableSetOf<Any>() val shallow = mutableSetOf<Any>()
@@ -48,7 +48,7 @@ class PartialLayoutTraversal(
try { try {
// If we encounter a node that has it own observer, don't traverse // If we encounter a node that has it own observer, don't traverse
if (node != root && treeObserverFactory.hasObserverFor(node)) { if (node != root && treeObserverFactory.hasObserverFor(node)) {
observableRoots.add(node) observableRoots.add((node to parentId))
continue continue
} }