Basic Litho support

Summary: Added an initial litho Tree observer and descriptors, its quiet naive and will be improved in a future diff

Reviewed By: lblasa

Differential Revision: D39466931

fbshipit-source-id: 66a462882af2e585b9719ee2f61595449f99c5e5
This commit is contained in:
Luke De Feo
2022-09-13 11:05:42 -07:00
committed by Facebook GitHub Bot
parent 24ec43eb92
commit 0562178739
8 changed files with 175 additions and 5 deletions

View File

@@ -6,6 +6,7 @@
*/
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion rootProject.compileSdkVersion

View File

@@ -0,0 +1,59 @@
/*
* 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.litho
import com.facebook.flipper.plugins.uidebugger.common.InspectableObject
import com.facebook.flipper.plugins.uidebugger.descriptors.Descriptor
import com.facebook.litho.DebugComponent
import com.facebook.litho.LithoView
object LithoViewDescriptor : Descriptor<LithoView>() {
override fun getId(node: LithoView): String = System.identityHashCode(node).toString()
override fun getName(node: LithoView): String = "LithoView"
override fun getChildren(node: LithoView, children: MutableList<Any>) {
val debugComponent = DebugComponent.getRootInstance(node)
if (debugComponent != null) {
children.add(debugComponent)
}
}
override fun getActiveChild(node: LithoView): Any? = null
override fun getData(node: LithoView, builder: MutableMap<String, InspectableObject>) {}
}
object DebugComponentDescriptor : Descriptor<DebugComponent>() {
override fun getId(node: DebugComponent): String = System.identityHashCode(node).toString()
override fun getName(node: DebugComponent): String {
return node.component.simpleName
}
// TODO the mutable list thing doesnt make sense for non chained descriptors, should just return
override fun getChildren(node: DebugComponent, children: MutableList<Any>) {
val mountedView = node.mountedView
val mountedDrawable = node.mountedDrawable
if (mountedView != null) {
children.add(mountedView)
} else if (mountedDrawable != null) {
children.add(mountedDrawable)
} else {
for (child in node.childComponents) {
children.add(child)
}
}
}
override fun getActiveChild(node: DebugComponent): Any? = null
// todo same here
override fun getData(node: DebugComponent, builder: MutableMap<String, InspectableObject>) {}
}

View File

@@ -0,0 +1,53 @@
package com.facebook.flipper.plugins.uidebugger.litho
import com.facebook.flipper.plugins.uidebugger.SubtreeUpdate
import com.facebook.flipper.plugins.uidebugger.TreeObserver
import com.facebook.flipper.plugins.uidebugger.core.Context
import com.facebook.flipper.plugins.uidebugger.identityHashCode
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverBuilder
import com.facebook.litho.LithoView
class LithoViewTreeObserver(val context: Context) : TreeObserver<LithoView>() {
var nodeRef: LithoView? = null
override fun subscribe(node: Any) {
node as LithoView
nodeRef = node
val listener: (view: LithoView) -> Unit = {
val start = System.currentTimeMillis()
val (nodes, skipped) = context.layoutTraversal.traverse(it)
for (observerRoot in skipped) {
if (!children.containsKey(observerRoot.identityHashCode())) {
val observer = context.observerFactory.createObserver(observerRoot, context)!!
observer.subscribe(observerRoot)
children[observerRoot.identityHashCode()] = observer
}
}
context.treeObserverManager.emit(
SubtreeUpdate("Litho", nodes, start, System.currentTimeMillis()))
}
node.setOnDirtyMountListener(listener)
listener(node)
}
override fun unsubscribe() {
nodeRef?.setOnDirtyMountListener(null)
nodeRef = null
}
}
object LithoViewTreeObserverBuilder : TreeObserverBuilder<LithoView> {
override fun canBuildFor(node: Any): Boolean {
return node is LithoView
}
override fun build(context: Context): TreeObserver<LithoView> {
return LithoViewTreeObserver(context)
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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.litho
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory
import com.facebook.litho.DebugComponent
import com.facebook.litho.LithoView
object UIDebuggerLithoSupport {
fun addDescriptors(register: DescriptorRegister) {
register.register(LithoView::class.java, LithoViewDescriptor)
register.register(DebugComponent::class.java, DebugComponentDescriptor)
}
fun addObserver(observerFactory: TreeObserverFactory) {
observerFactory.register(LithoViewTreeObserverBuilder)
}
}

View File

@@ -22,6 +22,9 @@ 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.descriptors.DescriptorRegister;
import com.facebook.flipper.plugins.uidebugger.litho.UIDebuggerLithoSupport;
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory;
import com.facebook.litho.config.ComponentsConfiguration;
import com.facebook.litho.editor.flipper.LithoFlipperDescriptors;
import java.util.Arrays;
@@ -56,7 +59,15 @@ public final class FlipperInitializer {
client.addPlugin(CrashReporterPlugin.getInstance());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(NavigationFlipperPlugin.getInstance());
client.addPlugin(new UIDebuggerFlipperPlugin((Application) context));
DescriptorRegister descriptorRegister = DescriptorRegister.Companion.withDefaults();
TreeObserverFactory treeObserverFactory = TreeObserverFactory.Companion.withDefaults();
UIDebuggerLithoSupport.INSTANCE.addDescriptors(descriptorRegister);
UIDebuggerLithoSupport.INSTANCE.addObserver(treeObserverFactory);
client.addPlugin(
new UIDebuggerFlipperPlugin(
(Application) context, descriptorRegister, treeObserverFactory));
client.start();
final OkHttpClient okHttpClient =

View File

@@ -21,14 +21,18 @@ import kotlinx.serialization.json.Json
const val LogTag = "FlipperUIDebugger"
class UIDebuggerFlipperPlugin(val application: Application) : FlipperPlugin {
class UIDebuggerFlipperPlugin(
val application: Application,
descriptorRegister: DescriptorRegister?,
observerFactory: TreeObserverFactory?
) : FlipperPlugin {
private val context: Context =
Context(
ApplicationRef(application),
ConnectionRef(null),
DescriptorRegister.withDefaults(),
TreeObserverFactory.withDefaults())
descriptorRegister = descriptorRegister ?: DescriptorRegister.withDefaults(),
observerFactory = observerFactory ?: TreeObserverFactory.withDefaults())
private val nativeScanScheduler = Scheduler(NativeScanScheduler(context))

View File

@@ -14,6 +14,7 @@ import com.facebook.flipper.plugins.uidebugger.LogTag
import com.facebook.flipper.plugins.uidebugger.SubtreeUpdate
import com.facebook.flipper.plugins.uidebugger.TreeObserver
import com.facebook.flipper.plugins.uidebugger.core.Context
import com.facebook.flipper.plugins.uidebugger.identityHashCode
typealias DecorView = View
@@ -40,6 +41,16 @@ class DecorViewObserver(val context: Context) : TreeObserver<DecorView>() {
val start = System.currentTimeMillis()
if (start - lastSend > throttleTimeMs) {
val (nodes, skipped) = context.layoutTraversal.traverse(node)
for (observerRoot in skipped) {
if (!children.containsKey(observerRoot.identityHashCode())) {
val observer = context.observerFactory.createObserver(observerRoot, context)!!
observer.subscribe(observerRoot)
children[observerRoot.identityHashCode()] = observer
}
}
val traversalComplete = System.currentTimeMillis()
context.treeObserverManager.emit(
SubtreeUpdate("DecorView", nodes, start, traversalComplete))

View File

@@ -8,6 +8,8 @@
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 org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -30,7 +32,11 @@ class UIDebuggerFlipperPluginTest {
@Throws(Exception::class)
@Test
fun emptyTest() {
var plugin = UIDebuggerFlipperPlugin(app)
var plugin =
UIDebuggerFlipperPlugin(
app,
DescriptorRegister.Companion.withDefaults(),
TreeObserverFactory.Companion.withDefaults())
Assert.assertNotNull(plugin)
}
}