For #8803: hook up frameworkStart metric.
parent
dbf733d70a
commit
f49fc6dad2
|
@ -38,6 +38,7 @@ import org.mozilla.fenix.FeatureFlags.webPushIntegration
|
||||||
import org.mozilla.fenix.components.Components
|
import org.mozilla.fenix.components.Components
|
||||||
import org.mozilla.fenix.components.metrics.MetricServiceType
|
import org.mozilla.fenix.components.metrics.MetricServiceType
|
||||||
import org.mozilla.fenix.ext.settings
|
import org.mozilla.fenix.ext.settings
|
||||||
|
import org.mozilla.fenix.perf.StartupTimeline
|
||||||
import org.mozilla.fenix.push.PushFxaIntegration
|
import org.mozilla.fenix.push.PushFxaIntegration
|
||||||
import org.mozilla.fenix.push.WebPushEngineIntegration
|
import org.mozilla.fenix.push.WebPushEngineIntegration
|
||||||
import org.mozilla.fenix.session.NotificationSessionObserver
|
import org.mozilla.fenix.session.NotificationSessionObserver
|
||||||
|
@ -49,6 +50,10 @@ import org.mozilla.fenix.utils.Settings
|
||||||
@SuppressLint("Registered")
|
@SuppressLint("Registered")
|
||||||
@Suppress("TooManyFunctions", "LargeClass")
|
@Suppress("TooManyFunctions", "LargeClass")
|
||||||
open class FenixApplication : LocaleAwareApplication() {
|
open class FenixApplication : LocaleAwareApplication() {
|
||||||
|
init {
|
||||||
|
recordOnInit() // DO NOT MOVE ANYTHING ABOVE HERE: the timing of this measurement is critical.
|
||||||
|
}
|
||||||
|
|
||||||
private val logger = Logger("FenixApplication")
|
private val logger = Logger("FenixApplication")
|
||||||
|
|
||||||
open val components by lazy { Components(this) }
|
open val components by lazy { Components(this) }
|
||||||
|
@ -365,4 +370,10 @@ open class FenixApplication : LocaleAwareApplication() {
|
||||||
Logger.error("Failed to initialize web extension support", e)
|
Logger.error("Failed to initialize web extension support", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun recordOnInit() {
|
||||||
|
// This gets called by more than one process. Ideally we'd only run this in the main process
|
||||||
|
// but the code to check which process we're in crashes because the Context isn't valid yet.
|
||||||
|
StartupTimeline.onApplicationInit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import mozilla.components.concept.engine.EngineView
|
||||||
import mozilla.components.feature.contextmenu.ext.DefaultSelectionActionDelegate
|
import mozilla.components.feature.contextmenu.ext.DefaultSelectionActionDelegate
|
||||||
import mozilla.components.service.fxa.sync.SyncReason
|
import mozilla.components.service.fxa.sync.SyncReason
|
||||||
import mozilla.components.support.base.feature.UserInteractionHandler
|
import mozilla.components.support.base.feature.UserInteractionHandler
|
||||||
|
import mozilla.components.support.ktx.android.arch.lifecycle.addObservers
|
||||||
import mozilla.components.support.ktx.android.content.share
|
import mozilla.components.support.ktx.android.content.share
|
||||||
import mozilla.components.support.ktx.kotlin.isUrl
|
import mozilla.components.support.ktx.kotlin.isUrl
|
||||||
import mozilla.components.support.ktx.kotlin.toNormalizedUrl
|
import mozilla.components.support.ktx.kotlin.toNormalizedUrl
|
||||||
|
@ -146,7 +147,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
|
||||||
}
|
}
|
||||||
supportActionBar?.hide()
|
supportActionBar?.hide()
|
||||||
|
|
||||||
lifecycle.addObserver(webExtensionPopupFeature)
|
lifecycle.addObservers(
|
||||||
|
webExtensionPopupFeature,
|
||||||
|
StartupTimeline.homeActivityLifecycleObserver
|
||||||
|
)
|
||||||
StartupTimeline.onActivityCreateEndHome(this)
|
StartupTimeline.onActivityCreateEndHome(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,17 @@
|
||||||
package org.mozilla.fenix.perf
|
package org.mozilla.fenix.perf
|
||||||
|
|
||||||
import androidx.annotation.UiThread
|
import androidx.annotation.UiThread
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleObserver
|
||||||
|
import androidx.lifecycle.OnLifecycleEvent
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.components.service.glean.private.NoReasonCodes
|
||||||
|
import mozilla.components.service.glean.private.PingType
|
||||||
|
import org.mozilla.fenix.GleanMetrics.Pings
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSiteItemViewHolder
|
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSiteItemViewHolder
|
||||||
|
import org.mozilla.fenix.perf.StartupTimeline.onApplicationInit
|
||||||
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupActivity
|
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupActivity
|
||||||
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination
|
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination
|
||||||
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState
|
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState
|
||||||
|
@ -20,12 +29,25 @@ import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState
|
||||||
* This class, and its dependencies, may need to be modified for any changes in startup.
|
* This class, and its dependencies, may need to be modified for any changes in startup.
|
||||||
*
|
*
|
||||||
* This class is not thread safe and should only be called from the main thread.
|
* This class is not thread safe and should only be called from the main thread.
|
||||||
|
*
|
||||||
|
* [onApplicationInit] is called from multiple processes. To minimize overhead, the class
|
||||||
|
* dependencies are lazily initialized.
|
||||||
*/
|
*/
|
||||||
@UiThread
|
@UiThread
|
||||||
object StartupTimeline {
|
object StartupTimeline {
|
||||||
|
|
||||||
private var state: StartupState = StartupState.Cold(StartupDestination.UNKNOWN)
|
private var state: StartupState = StartupState.Cold(StartupDestination.UNKNOWN)
|
||||||
private val reportFullyDrawn = StartupReportFullyDrawn()
|
|
||||||
|
private val reportFullyDrawn by lazy { StartupReportFullyDrawn() }
|
||||||
|
private val frameworkStartMeasurement by lazy { StartupFrameworkStartMeasurement() }
|
||||||
|
internal val homeActivityLifecycleObserver by lazy {
|
||||||
|
StartupHomeActivityLifecycleObserver(frameworkStartMeasurement)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onApplicationInit() {
|
||||||
|
// This gets called from multiple processes: don't do anything expensive. See call site for details.
|
||||||
|
frameworkStartMeasurement.onApplicationInit()
|
||||||
|
}
|
||||||
|
|
||||||
fun onActivityCreateEndIntentReceiver() {
|
fun onActivityCreateEndIntentReceiver() {
|
||||||
advanceState(StartupActivity.INTENT_RECEIVER)
|
advanceState(StartupActivity.INTENT_RECEIVER)
|
||||||
|
@ -45,3 +67,29 @@ object StartupTimeline {
|
||||||
state = StartupTimelineStateMachine.getNextState(state, startingActivity)
|
state = StartupTimelineStateMachine.getNextState(state, startingActivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [LifecycleObserver] for [HomeActivity] focused on startup performance measurement.
|
||||||
|
*/
|
||||||
|
internal class StartupHomeActivityLifecycleObserver(
|
||||||
|
private val frameworkStartMeasurement: StartupFrameworkStartMeasurement,
|
||||||
|
private val startupTimeline: PingType<NoReasonCodes> = Pings.startupTimeline
|
||||||
|
) : LifecycleObserver {
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||||
|
fun onStop() {
|
||||||
|
GlobalScope.launch { // use background thread due to expensive metrics.
|
||||||
|
// Ensure any last metrics are set before submission.
|
||||||
|
frameworkStartMeasurement.setExpensiveMetric()
|
||||||
|
|
||||||
|
// Startup metrics placed in the Activity should be re-recorded each time the Activity
|
||||||
|
// is started so we need to clear the ping lifetime by submitting once per each startup.
|
||||||
|
// It's less complex to add it here rather than the visual completeness task manager.
|
||||||
|
//
|
||||||
|
// N.B.: this submission location may need to be changed if we add metrics outside of the
|
||||||
|
// HomeActivity startup path (e.g. if the user goes directly to a separate activity and
|
||||||
|
// closes the app, they will never hit this) to appropriately adjust for the ping lifetimes.
|
||||||
|
startupTimeline.submit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,8 +13,9 @@ import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks
|
||||||
* An application class which knows how to migrate Fennec data.
|
* An application class which knows how to migrate Fennec data.
|
||||||
*/
|
*/
|
||||||
class MigratingFenixApplication : FenixApplication() {
|
class MigratingFenixApplication : FenixApplication() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
recordOnInit() // DO NOT MOVE ANYTHING ABOVE HERE: the timing of this measurement is critical.
|
||||||
|
|
||||||
PerformanceActivityLifecycleCallbacks.isTransientActivityInMigrationVariant = {
|
PerformanceActivityLifecycleCallbacks.isTransientActivityInMigrationVariant = {
|
||||||
if (it is MigrationDecisionActivity) true else false
|
if (it is MigrationDecisionActivity) true else false
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.perf
|
||||||
|
|
||||||
|
import io.mockk.MockKAnnotations
|
||||||
|
import io.mockk.impl.annotations.MockK
|
||||||
|
import io.mockk.verifySequence
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
|
import mozilla.components.service.glean.private.NoReasonCodes
|
||||||
|
import mozilla.components.service.glean.private.PingType
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
class StartupHomeActivityLifecycleObserverTest {
|
||||||
|
|
||||||
|
private lateinit var observer: StartupHomeActivityLifecycleObserver
|
||||||
|
@MockK(relaxed = true) private lateinit var frameworkStartMeasurement: StartupFrameworkStartMeasurement
|
||||||
|
@MockK(relaxed = true) private lateinit var startupTimeline: PingType<NoReasonCodes>
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockKAnnotations.init(this)
|
||||||
|
observer = StartupHomeActivityLifecycleObserver(frameworkStartMeasurement, startupTimeline)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN onStop is called THEN the metrics are set and the ping is submitted`() = runBlockingTest {
|
||||||
|
observer.onStop()
|
||||||
|
verifySequence {
|
||||||
|
frameworkStartMeasurement.setExpensiveMetric()
|
||||||
|
startupTimeline.submit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue