[fenix] For https://github.com/mozilla-mobile/fenix/issues/18426: add ApplicationStartReasonProvider.
We need to know this state to add a correct COLD MAIN probe.pull/600/head
parent
04af130922
commit
ee31fe29c2
@ -0,0 +1,101 @@
|
||||
/* 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 android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import mozilla.components.support.base.log.logger.Logger
|
||||
import org.mozilla.fenix.GleanMetrics.Metrics
|
||||
import org.mozilla.fenix.android.DefaultActivityLifecycleCallbacks
|
||||
|
||||
private val logger = Logger("AppStartReasonProvider")
|
||||
|
||||
/**
|
||||
* Provides the reason this [Application] instance was started: see [StartReason] for options
|
||||
* and [reason] for details.
|
||||
*
|
||||
* [registerInAppOnCreate] must be called for this class to work correctly.
|
||||
*
|
||||
* This class relies on specific lifecycle method call orders and main thread Runnable scheduling
|
||||
* that could potentially change between OEMs and OS versions: **be careful when using it.** This
|
||||
* implementation was tested on the Moto G5 Android 8.1.0 and the Pixel 2 Android 11.
|
||||
*/
|
||||
class AppStartReasonProvider {
|
||||
|
||||
enum class StartReason {
|
||||
/** We don't know yet what caused this [Application] instance to be started. */
|
||||
TO_BE_DETERMINED,
|
||||
|
||||
/** This [Application] instance was started due to an Activity trying to start. */
|
||||
ACTIVITY,
|
||||
|
||||
/**
|
||||
* This [Application] instance was started due to a component that is not an Activity:
|
||||
* this may include Services, BroadcastReceivers, and ContentProviders. It may be possible
|
||||
* to distinguish between these but it hasn't been necessary to do so yet.
|
||||
*/
|
||||
NON_ACTIVITY,
|
||||
}
|
||||
|
||||
/**
|
||||
* The reason this [Application] instance was started. This will not be set immediately
|
||||
* but is expected to be available by the time the first frame is drawn for the foreground Activity.
|
||||
*/
|
||||
var reason = StartReason.TO_BE_DETERMINED
|
||||
private set
|
||||
|
||||
/**
|
||||
* Registers the handlers needed by this class: this is expected to be called from
|
||||
* [Application.onCreate].
|
||||
*/
|
||||
fun registerInAppOnCreate(application: Application) {
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(ProcessLifecycleObserver())
|
||||
application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks())
|
||||
}
|
||||
|
||||
private inner class ProcessLifecycleObserver : DefaultLifecycleObserver {
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
// If the Application was started by an Activity, this Runnable should execute
|
||||
// after we learn the Activity was created. If the App was started by a Service,
|
||||
// this Runnable should execute before the first Activity is created.
|
||||
reason = when (reason) {
|
||||
StartReason.TO_BE_DETERMINED -> StartReason.NON_ACTIVITY
|
||||
StartReason.ACTIVITY -> reason /* the start reason is already known: do nothing. */
|
||||
StartReason.NON_ACTIVITY -> {
|
||||
Metrics.startReasonProcessError.set(true)
|
||||
logger.error("AppStartReasonProvider.Process...onCreate unexpectedly called twice")
|
||||
reason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
owner.lifecycle.removeObserver(this) // we don't update the state further.
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ActivityLifecycleCallbacks : DefaultActivityLifecycleCallbacks {
|
||||
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
|
||||
// See ProcessLifecycleObserver.onCreate for details.
|
||||
reason = when (reason) {
|
||||
StartReason.TO_BE_DETERMINED -> StartReason.ACTIVITY
|
||||
StartReason.NON_ACTIVITY -> reason /* the start reason is already known: do nothing. */
|
||||
StartReason.ACTIVITY -> {
|
||||
Metrics.startReasonActivityError.set(true)
|
||||
logger.error("AppStartReasonProvider.Activity...onCreate unexpectedly called twice")
|
||||
reason
|
||||
}
|
||||
}
|
||||
|
||||
activity.application.unregisterActivityLifecycleCallbacks(this) // we don't update the state further.
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue