Lightweight library designed to measure and collect performance metrics for Android applications in production.
Unlike other SaaS solutions (like Firebase Performance) it focuses only on collecting pure metrics and not enforces you to use specific reporting channel and monitoring infrastructure, so you're flexible with re-using the monitoring approaches already existing in your product.
Library supports collecting following performance metrics:
- App Cold Startup Time
- Rendering performance per Activity
- Time to Interactive & Time to First Render per screen
We recommend to read our blogpost "Measuring mobile apps performance in production" first to get some idea on what are these performance metrics, how they work and why those were chosen.
NOTE: You can also refer to the SampleApp in this repo to see a simplified example of how the library can be used in the real app
The library is available on Maven Central:
implementation("com.booking:perfsuite:0.3")
Implement the callback invoked once Startup Time is collected:
class MyStartupTimeListener : AppStartupTimeTracker.Listener {
override fun onColdStartupTimeIsReady(
startupTime: Long,
firstActivity: Activity,
isActualColdStart: Boolean
) {
// Log or report Startup Time metric in a preferable way
}
}
Then register your listener as early in Application#onCreate
as possible:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
AppStartupTimeTracker.register(this, MyStartupTimeListener())
}
}
Implement the callback invoked every time when the foreground Activity
is paused
(we can call it "the end of the screen session") and use RenderingMetricsMapper
to
represent rendering performance metrics
in a convenient aggregated format:
class MyFrameMetricsListener : ActivityFrameMetricsTracker.Listener {
override fun onFramesMetricsReady(
activity: Activity,
frameMetrics: Array<SparseIntArray>,
foregroundTime: Long?
) {
val data = RenderingMetricsMapper.toRenderingMetrics(frameMetrics, foregroundTime) ?: return
// Log or report Frame Metrics for current Activity's "session" in a preferable way
}
}
Then register your listener in Application#onCreate
before any activity is created:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
ActivityFrameMetricsTracker.register(this, MyFrameMetricsListener)
}
}
As per the code sample above you can use RenderingMetricsMapper
to collect frames metics in the aggreated format which is convenient for reporting to the backend.
Then metrics will be represented as RenderingMetrics
instance, which will provide data on:
totalFrames
- total amount of frames rendered during the screen sessiontotalFreezeTimeMs
- total accumulated time of the UI being frozen during the screen sessionslowFrames
- amount of slow frames per screens sessionfrozenFrames
- amount of frozen frames per screens session
Even though we support collecting widely used slow & frozen frames we strongly recommend relying on totalFreezeTimeMs
as the main rendering metric
Implement the callbacks invoked every time when screen's Time To Interactive (TTI) & Time To First Render (TTFR) metrics are collected:
object MyTtiListener : BaseTtiTracker.Listener {
override fun onScreenCreated(screen: String) {}
override fun onFirstFrameIsDrawn(screen: String, duration: Long) {
// Log or report TTFR metrics for specific screen in a preferable way
}
override fun onFirstUsableFrameIsDrawn(screen: String, duration: Long) {
// Log or report TTI metrics for specific screen in a preferable way
}
}
Then instantiate TTI tracker in Application#onCreate
before any activity is created and using this listener:
// keep instances globally accessible or inject as singletons using any preferable DI framework
val ttiTracker = BaseTtiTracker(AppTtiListener)
val viewTtiTracker = ViewTtiTracker(ttiTracker)
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
ActivityTtfrHelper.register(this, viewTtiTracker)
}
}
That will enable automatic TTFR collection for every Activity in the app.
For TTI collection you'll need to call viewTtiTracker.onScreenIsUsable(..)
manually from the Activity,
when the meaningful data is visible to the user e.g.:
// call this e.g. when the data is received from the backend,
// progress bar stops spinning and screen is fully ready for the user
viewTtiTracker.onScreenIsUsable(activity.componentName, rootContentView)
See the SampleApp for a full working example
The example above works for Activity
-based screens, however if you use the "Single-Activity" approach you also need
to enable TTI/TTFR tracking for the Fragments inside you main Activity:
class MyMainActivity : Activity() {
override fun onCreate() {
super.onCreate()
val fragmentHelper = FragmentTtfrHelper(viewTtiTracker)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentHelper, true)
}
}
Then you can call viewTtiTracker.onScreenIsUsable(..)
in Fragments the same way as described above.
- Measuring mobile apps performance in production
- App Startup Time documentation by Google
- Rendering Performance documentation by Google
- Android Vitals Articles by Pierre Yves Ricau
This software was originally developed at Booking.com. With approval from Booking.com, this software was released as open source, for which the authors would like to express their gratitude.