From 629884a352ec4659066cf3c15dbe7d3ebb680ff1 Mon Sep 17 00:00:00 2001 From: Laimonas Turauskas Date: Thu, 30 Nov 2023 13:04:46 -0800 Subject: [PATCH 1/2] [Formula Android] Track first render. --- .../android/compose/ComposeViewFactory.kt | 17 +++++++++++++---- .../instacart/formula/android/FeatureView.kt | 1 + .../formula/android/FormulaFragment.kt | 4 ++++ .../formula/android/FragmentEnvironment.kt | 6 ++++++ .../android/views/FeatureViewBindFunction.kt | 8 ++++++++ 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt b/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt index f0c1bcd1..fdd302fa 100644 --- a/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt +++ b/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt @@ -14,19 +14,28 @@ abstract class ComposeViewFactory : ViewFactory { override fun create(inflater: LayoutInflater, container: ViewGroup?): FeatureView { val view = ComposeView(inflater.context) + var firstRender = true return FeatureView( view = view, - bind = { + bind = { state -> view.setContent { - val model = it.observable.subscribeAsState(null).value + val model = state.observable.subscribeAsState(null).value if (model != null) { val start = SystemClock.uptimeMillis() Content(model) val end = SystemClock.uptimeMillis() - it.environment.eventListener?.onRendered( - fragmentId = it.fragmentId, + state.environment.eventListener?.onRendered( + fragmentId = state.fragmentId, durationInMillis = end - start, ) + + if (firstRender) { + firstRender = false + state.environment.eventListener?.onFirstModelRendered( + fragmentId = state.fragmentId, + durationInMillis = end - state.initialized, + ) + } } } null diff --git a/formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt b/formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt index b2ced889..18022a5c 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt @@ -22,6 +22,7 @@ class FeatureView( val lifecycleCallbacks: FragmentLifecycleCallback? = null, ) { class State( + val initialized: Long, val fragmentId: FragmentId, val environment: FragmentEnvironment, val observable: Observable, diff --git a/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt b/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt index 0c0e421e..537ea44d 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt @@ -1,6 +1,7 @@ package com.instacart.formula.android import android.os.Bundle +import android.os.SystemClock import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -29,6 +30,8 @@ class FormulaFragment : Fragment(), BaseFormulaFragment { requireArguments().getParcelable(ARG_CONTRACT)!! } + private val initialized = SystemClock.uptimeMillis() + private var featureView: FeatureView? = null private val stateRelay: BehaviorRelay = BehaviorRelay.create() private var cancelable: Cancelable? = null @@ -50,6 +53,7 @@ class FormulaFragment : Fragment(), BaseFormulaFragment { super.onViewCreated(view, savedInstanceState) featureView?.let { value -> val state = FeatureView.State( + initialized = initialized, fragmentId = getFormulaFragmentId(), environment = FormulaFragmentDelegate.fragmentEnvironment(), observable = stateRelay, diff --git a/formula-android/src/main/java/com/instacart/formula/android/FragmentEnvironment.kt b/formula-android/src/main/java/com/instacart/formula/android/FragmentEnvironment.kt index f907b611..d284d412 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/FragmentEnvironment.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/FragmentEnvironment.kt @@ -25,5 +25,11 @@ data class FragmentEnvironment( * Called after render model was applied to the [FeatureView]. */ fun onRendered(fragmentId: FragmentId, durationInMillis: Long) + + /** + * Called after first render model is rendered. The [durationInMillis] starts + * when formula fragment is initialized and ends after first render model is applied. + */ + fun onFirstModelRendered(fragmentId: FragmentId, durationInMillis: Long) } } diff --git a/formula-android/src/main/java/com/instacart/formula/android/views/FeatureViewBindFunction.kt b/formula-android/src/main/java/com/instacart/formula/android/views/FeatureViewBindFunction.kt index 4b29a51c..bffeca27 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/views/FeatureViewBindFunction.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/views/FeatureViewBindFunction.kt @@ -14,6 +14,7 @@ internal class FeatureViewBindFunction( ) : (FeatureView.State) -> Cancelable? { override fun invoke(state: FeatureView.State): Cancelable { val environment = state.environment + var firstRender = true val disposable = state.observable.subscribe { try { val start = SystemClock.uptimeMillis() @@ -23,6 +24,13 @@ internal class FeatureViewBindFunction( fragmentId = state.fragmentId, durationInMillis = end - start, ) + if (firstRender) { + firstRender = false + environment.eventListener?.onFirstModelRendered( + fragmentId = state.fragmentId, + durationInMillis = end - state.initialized, + ) + } } catch (exception: Exception) { environment.onScreenError(state.fragmentId.key, exception) } From 275064f57b656e5cf54409a41ec1b2739dfbde1f Mon Sep 17 00:00:00 2001 From: Laimonas Turauskas Date: Thu, 30 Nov 2023 13:18:34 -0800 Subject: [PATCH 2/2] PR feedback. --- .../instacart/formula/android/compose/ComposeViewFactory.kt | 2 +- .../main/java/com/instacart/formula/android/FeatureView.kt | 2 +- .../java/com/instacart/formula/android/FormulaFragment.kt | 4 ++-- .../formula/android/views/FeatureViewBindFunction.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt b/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt index fdd302fa..e32a4573 100644 --- a/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt +++ b/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt @@ -33,7 +33,7 @@ abstract class ComposeViewFactory : ViewFactory { firstRender = false state.environment.eventListener?.onFirstModelRendered( fragmentId = state.fragmentId, - durationInMillis = end - state.initialized, + durationInMillis = end - state.initializedAtMillis, ) } } diff --git a/formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt b/formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt index 18022a5c..f4b24a53 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt @@ -22,7 +22,7 @@ class FeatureView( val lifecycleCallbacks: FragmentLifecycleCallback? = null, ) { class State( - val initialized: Long, + val initializedAtMillis: Long, val fragmentId: FragmentId, val environment: FragmentEnvironment, val observable: Observable, diff --git a/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt b/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt index 537ea44d..2ad51618 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt @@ -30,7 +30,7 @@ class FormulaFragment : Fragment(), BaseFormulaFragment { requireArguments().getParcelable(ARG_CONTRACT)!! } - private val initialized = SystemClock.uptimeMillis() + private val initializedAtMillis = SystemClock.uptimeMillis() private var featureView: FeatureView? = null private val stateRelay: BehaviorRelay = BehaviorRelay.create() @@ -53,7 +53,7 @@ class FormulaFragment : Fragment(), BaseFormulaFragment { super.onViewCreated(view, savedInstanceState) featureView?.let { value -> val state = FeatureView.State( - initialized = initialized, + initializedAtMillis = initializedAtMillis, fragmentId = getFormulaFragmentId(), environment = FormulaFragmentDelegate.fragmentEnvironment(), observable = stateRelay, diff --git a/formula-android/src/main/java/com/instacart/formula/android/views/FeatureViewBindFunction.kt b/formula-android/src/main/java/com/instacart/formula/android/views/FeatureViewBindFunction.kt index bffeca27..56f083e5 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/views/FeatureViewBindFunction.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/views/FeatureViewBindFunction.kt @@ -28,7 +28,7 @@ internal class FeatureViewBindFunction( firstRender = false environment.eventListener?.onFirstModelRendered( fragmentId = state.fragmentId, - durationInMillis = end - state.initialized, + durationInMillis = end - state.initializedAtMillis, ) } } catch (exception: Exception) {