From 914b32c383098882fed696dbaf2a98af4b195c89 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 1 Mar 2023 08:49:49 -0800 Subject: [PATCH] Add framework event infra Summary: Added infra for collecting events from UI frameworks. 1. Framework event metadata captures all the static metadata around the event. This allows to us to not send the same metadata in every event as well as populate the monitoring drop down immediately. This is sent in init since this information is static 2. Framework event itself is quite bare at the moment. It will have thread and more attributes in the future The UIdebugger litho support ulitity has been simplified now there are 3 extension points. Context renamed to UIDContext since it is referenced in app initialisers where the android context is also imported and it create a naming collision Reviewed By: lblasa Differential Revision: D42606933 fbshipit-source-id: a419f3fd424c533d586813004c40b68feafd9a2e --- .../litho/UIDebuggerLithoSupportStub.kt | 7 ++---- .../flipper/sample/FlipperInitializer.java | 9 ++++---- .../uidebugger/UIDebuggerFlipperPlugin.kt | 20 ++++------------ .../plugins/uidebugger/commands/Command.kt | 4 ++-- .../core/{Context.kt => UIDContext.kt} | 16 ++++++++++++- .../plugins/uidebugger/model/Events.kt | 7 +++--- .../uidebugger/model/FrameworkEvents.kt | 23 +++++++++++++++++++ .../observers/ApplicationTreeObserver.kt | 4 ++-- .../observers/DecorViewTreeObserver.kt | 6 ++--- .../uidebugger/observers/TreeObserver.kt | 9 +++++--- .../observers/TreeObserverFactory.kt | 6 ++--- .../observers/TreeObserverManager.kt | 16 ++++++++++--- .../uidebugger/UIDebuggerFlipperPluginTest.kt | 9 ++------ 13 files changed, 82 insertions(+), 54 deletions(-) rename android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/{Context.kt => UIDContext.kt} (68%) create mode 100644 android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/FrameworkEvents.kt diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupportStub.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupportStub.kt index ba428ef3b..a6241e0f9 100644 --- a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupportStub.kt +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupportStub.kt @@ -7,13 +7,10 @@ package com.facebook.flipper.plugins.uidebugger.litho -import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister -import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory +import com.facebook.flipper.plugins.uidebugger.core.UIDContext // this is not used internally object UIDebuggerLithoSupport { - fun addDescriptors(register: DescriptorRegister) {} - - fun addObserver(observerFactory: TreeObserverFactory) {} + fun enable(context: UIDContext) {} } diff --git a/android/sample/src/debug/java/com/facebook/flipper/sample/FlipperInitializer.java b/android/sample/src/debug/java/com/facebook/flipper/sample/FlipperInitializer.java index 47aeb7353..fe1c38749 100644 --- a/android/sample/src/debug/java/com/facebook/flipper/sample/FlipperInitializer.java +++ b/android/sample/src/debug/java/com/facebook/flipper/sample/FlipperInitializer.java @@ -22,6 +22,7 @@ import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin.SharedPreferencesDescriptor; import com.facebook.flipper.plugins.uidebugger.UIDebuggerFlipperPlugin; +import com.facebook.flipper.plugins.uidebugger.core.UIDContext; import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister; import com.facebook.flipper.plugins.uidebugger.litho.UIDebuggerLithoSupport; import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory; @@ -62,12 +63,10 @@ public final class FlipperInitializer { DescriptorRegister descriptorRegister = DescriptorRegister.Companion.withDefaults(); TreeObserverFactory treeObserverFactory = TreeObserverFactory.Companion.withDefaults(); - UIDebuggerLithoSupport.INSTANCE.addDescriptors(descriptorRegister); - UIDebuggerLithoSupport.INSTANCE.addObserver(treeObserverFactory); + UIDContext uidContext = UIDContext.Companion.create((Application) context); + UIDebuggerLithoSupport.INSTANCE.enable(uidContext); - client.addPlugin( - new UIDebuggerFlipperPlugin( - (Application) context, descriptorRegister, treeObserverFactory)); + client.addPlugin(new UIDebuggerFlipperPlugin(uidContext)); client.start(); final OkHttpClient okHttpClient = diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt index b5a5a1d66..791f3fed5 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt @@ -7,33 +7,19 @@ package com.facebook.flipper.plugins.uidebugger -import android.app.Application import android.util.Log import com.facebook.flipper.core.FlipperConnection import com.facebook.flipper.core.FlipperPlugin import com.facebook.flipper.plugins.uidebugger.core.* import com.facebook.flipper.plugins.uidebugger.descriptors.ApplicationRefDescriptor -import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister import com.facebook.flipper.plugins.uidebugger.model.InitEvent import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent -import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory import kotlinx.serialization.json.Json const val LogTag = "ui-debugger" -class UIDebuggerFlipperPlugin( - val application: Application, - descriptorRegister: DescriptorRegister?, - observerFactory: TreeObserverFactory? -) : FlipperPlugin { - - private val context: Context = - Context( - ApplicationRef(application), - ConnectionRef(null), - descriptorRegister = descriptorRegister ?: DescriptorRegister.withDefaults(), - observerFactory = observerFactory ?: TreeObserverFactory.withDefaults()) +class UIDebuggerFlipperPlugin(val context: UIDContext) : FlipperPlugin { init { Log.i(LogTag, "Initializing ui-debugger") @@ -53,7 +39,9 @@ class UIDebuggerFlipperPlugin( InitEvent.name, Json.encodeToString( InitEvent.serializer(), - InitEvent(ApplicationRefDescriptor.getId(context.applicationRef)))) + InitEvent( + ApplicationRefDescriptor.getId(context.applicationRef), + context.frameworkEventMetadata))) connection.send( MetadataUpdateEvent.name, diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/commands/Command.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/commands/Command.kt index b442ef599..5a79866e5 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/commands/Command.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/commands/Command.kt @@ -11,10 +11,10 @@ import com.facebook.flipper.core.FlipperObject import com.facebook.flipper.core.FlipperReceiver import com.facebook.flipper.core.FlipperResponder import com.facebook.flipper.plugins.common.MainThreadFlipperReceiver -import com.facebook.flipper.plugins.uidebugger.core.Context +import com.facebook.flipper.plugins.uidebugger.core.UIDContext /** An interface for extensions to the UIDebugger plugin */ -abstract class Command(val context: Context) { +abstract class Command(val context: UIDContext) { /** The command identifier to respond to */ abstract fun identifier(): String /** Execute the command */ diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/Context.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UIDContext.kt similarity index 68% rename from android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/Context.kt rename to android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UIDContext.kt index 28a701bc8..3142b0193 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/Context.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UIDContext.kt @@ -7,19 +7,22 @@ package com.facebook.flipper.plugins.uidebugger.core +import android.app.Application import com.facebook.flipper.core.FlipperConnection import com.facebook.flipper.plugins.uidebugger.common.BitmapPool import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister +import com.facebook.flipper.plugins.uidebugger.model.FrameworkEventMetadata import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverManager import com.facebook.flipper.plugins.uidebugger.scheduler.SharedThrottle import com.facebook.flipper.plugins.uidebugger.traversal.PartialLayoutTraversal -data class Context( +data class UIDContext( val applicationRef: ApplicationRef, val connectionRef: ConnectionRef, val descriptorRegister: DescriptorRegister, val observerFactory: TreeObserverFactory, + val frameworkEventMetadata: MutableList ) { val layoutTraversal: PartialLayoutTraversal = PartialLayoutTraversal(descriptorRegister, observerFactory) @@ -27,6 +30,17 @@ data class Context( val treeObserverManager = TreeObserverManager(this) val sharedThrottle: SharedThrottle = SharedThrottle() val bitmapPool = BitmapPool() + + companion object { + fun create(application: Application): UIDContext { + return UIDContext( + ApplicationRef(application), + ConnectionRef(null), + descriptorRegister = DescriptorRegister.withDefaults(), + observerFactory = TreeObserverFactory.withDefaults(), + frameworkEventMetadata = mutableListOf()) + } + } } data class ConnectionRef(var connection: FlipperConnection?) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Events.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Events.kt index 7d39bb1dc..4fca29a9b 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Events.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Events.kt @@ -10,9 +10,7 @@ package com.facebook.flipper.plugins.uidebugger.model import com.facebook.flipper.plugins.uidebugger.descriptors.Id @kotlinx.serialization.Serializable -data class InitEvent( - val rootId: Id, -) { +data class InitEvent(val rootId: Id, val frameworkEventMetadata: List) { companion object { const val name = "init" } @@ -31,7 +29,8 @@ data class SubtreeUpdateEvent( val observerType: String, val rootId: Id, val nodes: List, - val snapshot: String? = null + val snapshot: String?, + val frameworkEvents: List? ) { companion object { const val name = "subtreeUpdate" diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/FrameworkEvents.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/FrameworkEvents.kt new file mode 100644 index 000000000..76ebbea04 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/FrameworkEvents.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.model + +import com.facebook.flipper.plugins.uidebugger.descriptors.Id + +@kotlinx.serialization.Serializable +data class FrameworkEventMetadata( + val type: String, + val documentation: String, +) + +@kotlinx.serialization.Serializable +data class FrameworkEvent( + val nodeId: Id, + val type: String, + val timestamp: Long, +) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/ApplicationTreeObserver.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/ApplicationTreeObserver.kt index 6c81b1f6b..45587ac50 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/ApplicationTreeObserver.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/ApplicationTreeObserver.kt @@ -11,15 +11,15 @@ import android.util.Log import android.view.View import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef -import com.facebook.flipper.plugins.uidebugger.core.Context import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver +import com.facebook.flipper.plugins.uidebugger.core.UIDContext import com.facebook.flipper.plugins.uidebugger.util.objectIdentity /** * Responsible for observing the activity stack and managing the subscription to the top most * content view (decor view) */ -class ApplicationTreeObserver(val context: Context) : TreeObserver() { +class ApplicationTreeObserver(val context: UIDContext) : TreeObserver() { override val type = "Application" diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/DecorViewTreeObserver.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/DecorViewTreeObserver.kt index 72b92e754..e036ea4f3 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/DecorViewTreeObserver.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/DecorViewTreeObserver.kt @@ -12,14 +12,14 @@ import android.view.View import android.view.ViewTreeObserver import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.common.BitmapPool -import com.facebook.flipper.plugins.uidebugger.core.Context +import com.facebook.flipper.plugins.uidebugger.core.UIDContext import com.facebook.flipper.plugins.uidebugger.util.objectIdentity import java.lang.ref.WeakReference typealias DecorView = View /** Responsible for subscribing to updates to the content view of an activity */ -class DecorViewObserver(val context: Context) : TreeObserver() { +class DecorViewObserver(val context: UIDContext) : TreeObserver() { private var nodeRef: WeakReference? = null private var preDrawListener: ViewTreeObserver.OnPreDrawListener? = null @@ -78,7 +78,7 @@ object DecorViewTreeObserverBuilder : TreeObserverBuilder { return node.javaClass.simpleName.contains("DecorView") } - override fun build(context: Context): TreeObserver { + override fun build(context: UIDContext): TreeObserver { Log.i(LogTag, "Building DecorView observer") return DecorViewObserver(context) } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserver.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserver.kt index b1ab98226..8cab32093 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserver.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserver.kt @@ -10,9 +10,10 @@ package com.facebook.flipper.plugins.uidebugger.observers import android.util.Log import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.common.BitmapPool -import com.facebook.flipper.plugins.uidebugger.core.Context +import com.facebook.flipper.plugins.uidebugger.core.UIDContext import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor +import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent import com.facebook.flipper.plugins.uidebugger.util.objectIdentity /* @@ -40,9 +41,10 @@ abstract class TreeObserver { /** Traverses the layout hierarchy while managing any encountered child observers. */ fun traverseAndSend( - context: Context, + context: UIDContext, root: Any, - snapshotBitmap: BitmapPool.ReusableBitmap? = null + snapshotBitmap: BitmapPool.ReusableBitmap? = null, + frameworkEvents: List? = null ) { val startTimestamp = System.currentTimeMillis() val (visitedNodes, observableRoots) = context.layoutTraversal.traverse(root) @@ -97,6 +99,7 @@ abstract class TreeObserver { startTimestamp, traversalCompleteTime, snapshotCompleteTime, + frameworkEvents, snapshotBitmap)) } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserverFactory.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserverFactory.kt index b4b0bd5fa..769a09813 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserverFactory.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserverFactory.kt @@ -7,11 +7,11 @@ package com.facebook.flipper.plugins.uidebugger.observers -import com.facebook.flipper.plugins.uidebugger.core.Context +import com.facebook.flipper.plugins.uidebugger.core.UIDContext interface TreeObserverBuilder { fun canBuildFor(node: Any): Boolean - fun build(context: Context): TreeObserver + fun build(context: UIDContext): TreeObserver } class TreeObserverFactory { @@ -28,7 +28,7 @@ class TreeObserverFactory { } // TODO: Not very efficient, need to cache this. Builders cannot be removed. - fun createObserver(node: Any, context: Context): TreeObserver<*>? { + fun createObserver(node: Any, context: UIDContext): TreeObserver<*>? { return builders.find { it.canBuildFor(node) }?.build(context) } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserverManager.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserverManager.kt index 2e3649ab4..17f8545ad 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserverManager.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserverManager.kt @@ -16,9 +16,10 @@ import android.util.Log import android.view.Choreographer import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.common.BitmapPool -import com.facebook.flipper.plugins.uidebugger.core.Context +import com.facebook.flipper.plugins.uidebugger.core.UIDContext import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister +import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent import com.facebook.flipper.plugins.uidebugger.model.Node import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent @@ -37,13 +38,14 @@ data class SubtreeUpdate( val startTime: Long, val traversalCompleteTime: Long, val snapshotComplete: Long, + val frameworkEvents: List?, val snapshot: BitmapPool.ReusableBitmap? ) data class BatchedUpdate(val updates: List, val frameTimeMs: Long) /** Holds the root observer and manages sending updates to desktop */ -class TreeObserverManager(val context: Context) { +class TreeObserverManager(val context: UIDContext) { private val rootObserver = ApplicationTreeObserver(context) private lateinit var batchedUpdates: Channel @@ -104,6 +106,7 @@ class TreeObserverManager(val context: Context) { val onWorkerThread = System.currentTimeMillis() val nodes = batchedUpdate.updates.flatMap { it.deferredNodes.map { it.value() } } + val frameworkEvents = batchedUpdate.updates.flatMap { it.frameworkEvents ?: listOf() } val snapshotUpdate = batchedUpdate.updates.find { it.snapshot != null } val deferredComptationComplete = System.currentTimeMillis() @@ -116,13 +119,20 @@ class TreeObserverManager(val context: Context) { snapshotUpdate.snapshot.readyForReuse() } + // it is important this comes after deferred processing since the deferred processing can create + // metadata sendMetadata() val serialized = Json.encodeToString( SubtreeUpdateEvent.serializer(), SubtreeUpdateEvent( - batchedUpdate.frameTimeMs, "batched", snapshotUpdate?.rootId ?: 1, nodes, snapshot)) + batchedUpdate.frameTimeMs, + "batched", + snapshotUpdate?.rootId ?: 1, + nodes, + snapshot, + frameworkEvents)) val serializationEnd = System.currentTimeMillis() diff --git a/android/src/test/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPluginTest.kt b/android/src/test/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPluginTest.kt index a8bda3c79..a7ba21948 100644 --- a/android/src/test/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPluginTest.kt +++ b/android/src/test/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPluginTest.kt @@ -8,8 +8,7 @@ package com.facebook.flipper.plugins.uidebugger import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef -import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister -import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory +import com.facebook.flipper.plugins.uidebugger.core.UIDContext import org.junit.Assert import org.junit.Before import org.junit.Test @@ -32,11 +31,7 @@ class UIDebuggerFlipperPluginTest { @Throws(Exception::class) @Test fun emptyTest() { - var plugin = - UIDebuggerFlipperPlugin( - app, - DescriptorRegister.Companion.withDefaults(), - TreeObserverFactory.Companion.withDefaults()) + var plugin = UIDebuggerFlipperPlugin(UIDContext.create(app)) Assert.assertNotNull(plugin) } }