Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Jank performance measurement #754

Merged
merged 1 commit into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions feature/performance/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation dagger

implementation 'com.jakewharton.timber:timber:5.0.1'
implementation "androidx.metrics:metrics-performance:1.0.0-alpha03"

testImplementation 'junit:junit:4.13.2'
testImplementation 'org.assertj:assertj-core:3.23.1'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.jraska.github.client.performance

import com.jraska.github.client.core.android.OnAppCreate
import com.jraska.github.client.performance.jank.JankMetric
import com.jraska.github.client.performance.startup.StartupAnalyticsReporter
import com.jraska.github.client.performance.startup.StartupTimeMetric
import dagger.Module
Expand All @@ -19,4 +20,8 @@ object PerformanceModule {
internal fun startupTimeMetric(reporter: StartupAnalyticsReporter): StartupTimeMetric {
return StartupTimeMetric(reporter)
}

@Provides
@IntoSet
internal fun startupJank(create: JankMetric.StartupOnCreate): OnAppCreate = create
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.jraska.github.client.performance.jank

import androidx.metrics.performance.FrameData
import com.jraska.github.client.Owner
import com.jraska.github.client.analytics.AnalyticsEvent
import com.jraska.github.client.analytics.EventAnalytics
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import java.util.concurrent.TimeUnit.NANOSECONDS
import javax.inject.Inject

private val TOO_LARGE_JANK_NANOS = NANOSECONDS.convert(3 * 16, MILLISECONDS)
private val JANK_ANALYTICS_NAME = AnalyticsEvent.Key("performance_ui_jank", Owner.PERFORMANCE_TEAM)
private const val JANK_TIME_PROPERTY = "value"

class JankAnalyticsReporter @Inject constructor(
private val eventAnalytics: EventAnalytics
) {
fun reportJank(frame: FrameData) {
Timber.v("Jank of %s", frame)

if (frame.frameDurationUiNanos >= TOO_LARGE_JANK_NANOS) {
reportJankToAnalytics(frame)
}
}

private fun reportJankToAnalytics(frame: FrameData) {
val jankMs = NANOSECONDS.toMillis(frame.frameDurationUiNanos)

val event = AnalyticsEvent.builder(JANK_ANALYTICS_NAME)
.addProperty(JANK_TIME_PROPERTY, jankMs)
.build()

eventAnalytics.report(event)
Timber.d("Jank of %s ms reported to analytics", jankMs)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.jraska.github.client.performance.jank

import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.metrics.performance.FrameData
import androidx.metrics.performance.JankStats
import com.jraska.github.client.core.android.DefaultActivityCallbacks
import com.jraska.github.client.core.android.OnAppCreate
import javax.inject.Inject

internal class JankMetric @Inject constructor(
private val jankAnalyticsReporter: JankAnalyticsReporter
) : DefaultActivityCallbacks() {
private val onFrameListener = JankStats.OnFrameListener { onFrame(it) }

private fun onAppCreate(app: Application) {
app.registerActivityLifecycleCallbacks(object : DefaultActivityCallbacks() {
private var creatingActivity: Activity? =
null // store this as we cannot call `createAndTrack` in onActivityCreated and `onActivityStarted` will be called multiple times

override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
super.onActivityCreated(activity, savedInstanceState)
creatingActivity = activity
}

override fun onActivityStarted(activity: Activity) {
super.onActivityStarted(activity)

if (creatingActivity == activity) {
JankStats.createAndTrack(activity.window, onFrameListener)
}
creatingActivity = null
}
})
}

private fun onFrame(frameData: FrameData) {
if (frameData.isJank) {
jankAnalyticsReporter.reportJank(frameData)
}
}

internal class StartupOnCreate @Inject constructor(
private val jankMetric: JankMetric
) : OnAppCreate {
override fun onCreate(app: Application) {
jankMetric.onAppCreate(app)
}
}
}