Skip to content

Commit a10b1e1

Browse files
committed
[feature/#719] Add baselineprofile module
[feature/#719] Add baseline-prof when variant is dev [feature/#719] Delete baseline-prof
1 parent b4d8b4d commit a10b1e1

File tree

10 files changed

+221
-1
lines changed

10 files changed

+221
-1
lines changed

app-android/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ plugins {
1111
id("droidkaigi.primitive.android.roborazzi")
1212
id("droidkaigi.primitive.kover")
1313
id("droidkaigi.primitive.osslicenses")
14+
alias(libs.plugins.androidx.baselineprofile)
1415
}
1516

1617
val keystorePropertiesFile = file("keystore.properties")
@@ -118,6 +119,8 @@ dependencies {
118119
// Dependency configuration to aggregate Kover coverage reports
119120
// TODO: extract report aggregation to build-logic
120121
dependencies {
122+
baselineProfile(projects.baselineprofile)
123+
implementation(libs.profileinstaller)
121124
kover(projects.appIosShared)
122125

123126
kover(projects.feature.about)

baselineprofile/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

baselineprofile/build.gradle.kts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import com.android.build.api.dsl.ManagedVirtualDevice
2+
3+
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
4+
plugins {
5+
alias(libs.plugins.androidTest)
6+
alias(libs.plugins.kotlinGradlePlugin)
7+
alias(libs.plugins.androidx.baselineprofile)
8+
}
9+
10+
android {
11+
namespace = "io.github.droidkaigi.confsched2023"
12+
compileSdk = 34
13+
14+
compileOptions {
15+
sourceCompatibility = JavaVersion.VERSION_11
16+
targetCompatibility = JavaVersion.VERSION_11
17+
}
18+
19+
kotlinOptions {
20+
jvmTarget = "11"
21+
}
22+
23+
defaultConfig {
24+
minSdk = 28
25+
targetSdk = 34
26+
27+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
28+
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR"
29+
}
30+
31+
targetProjectPath = ":app-android"
32+
33+
flavorDimensions += listOf("network")
34+
productFlavors {
35+
create("dev") { dimension = "network" }
36+
create("prod") { dimension = "network" }
37+
}
38+
39+
testOptions.managedDevices.devices {
40+
create<ManagedVirtualDevice>("pixel6Api34") {
41+
device = "Pixel 6"
42+
apiLevel = 34
43+
systemImageSource = "google"
44+
}
45+
}
46+
47+
buildFeatures {
48+
buildConfig = true
49+
}
50+
}
51+
52+
// This is the configuration block for the Baseline Profile plugin.
53+
// You can specify to run the generators on a managed devices or connected devices.
54+
baselineProfile {
55+
managedDevices += "pixel6Api34"
56+
useConnectedDevices = false
57+
}
58+
59+
dependencies {
60+
implementation(libs.androidxTestExtJunit)
61+
implementation(libs.androidxTestEspressoEspressoCore)
62+
implementation(libs.uiautomator)
63+
implementation(libs.benchmark.macro.junit4)
64+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2+
<queries>
3+
<package android:name="io.github.droidkaigi.confsched2023" />
4+
</queries>
5+
</manifest>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.github.droidkaigi.confsched2023
2+
3+
import androidx.benchmark.macro.junit4.BaselineProfileRule
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
import androidx.test.filters.LargeTest
6+
import org.junit.Rule
7+
import org.junit.Test
8+
import org.junit.runner.RunWith
9+
10+
/**
11+
* This test class generates a basic startup baseline profile for the target package.
12+
*
13+
* We recommend you start with this but add important user flows to the profile to improve their performance.
14+
* Refer to the [baseline profile documentation](https://d.android.com/topic/performance/baselineprofiles)
15+
* for more information.
16+
*
17+
* You can run the generator with the Generate Baseline Profile run configuration,
18+
* or directly with `generateBaselineProfile` Gradle task:
19+
* ```
20+
* ./gradlew :app-android:generateReleaseBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
21+
* ```
22+
* The run configuration runs the Gradle task and applies filtering to run only the generators.
23+
*
24+
* Check [documentation](https://d.android.com/topic/performance/benchmarking/macrobenchmark-instrumentation-args)
25+
* for more information about available instrumentation arguments.
26+
*
27+
* After you run the generator, you can verify the improvements running the [StartupBenchmarks] benchmark.
28+
**/
29+
@RunWith(AndroidJUnit4::class)
30+
@LargeTest
31+
class BaselineProfileGenerator {
32+
33+
@get:Rule
34+
val rule = BaselineProfileRule()
35+
36+
@Test
37+
fun generate() {
38+
rule.collect(PACKAGE_NAME) {
39+
// This block defines the app's critical user journey. Here we are interested in
40+
// optimizing for app startup. But you can also navigate and scroll
41+
// through your most important UI.
42+
43+
// Start default activity for your app
44+
pressHome()
45+
startActivityAndWait()
46+
47+
// TODO Write more interactions to optimize advanced journeys of your app.
48+
// For example:
49+
// 1. Wait until the content is asynchronously loaded
50+
// 2. Scroll the feed content
51+
// 3. Navigate to detail screen
52+
53+
// Check UiAutomator documentation for more information how to interact with the app.
54+
// https://d.android.com/training/testing/other-components/ui-automator
55+
}
56+
}
57+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package io.github.droidkaigi.confsched2023
2+
3+
val PACKAGE_NAME = buildString {
4+
append("io.github.droidkaigi.confsched2023")
5+
append(if (BuildConfig.FLAVOR == "dev") ".dev" else "")
6+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.github.droidkaigi.confsched2023
2+
3+
import androidx.benchmark.macro.BaselineProfileMode
4+
import androidx.benchmark.macro.CompilationMode
5+
import androidx.benchmark.macro.StartupMode
6+
import androidx.benchmark.macro.StartupTimingMetric
7+
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
8+
import androidx.test.ext.junit.runners.AndroidJUnit4
9+
import androidx.test.filters.LargeTest
10+
import org.junit.Rule
11+
import org.junit.Test
12+
import org.junit.runner.RunWith
13+
14+
/**
15+
* This test class benchmarks the speed of app startup.
16+
* Run this benchmark to verify how effective a Baseline Profile is.
17+
* It does this by comparing [CompilationMode.None], which represents the app with no Baseline
18+
* Profiles optimizations, and [CompilationMode.Partial], which uses Baseline Profiles.
19+
*
20+
* Run this benchmark to see startup measurements and captured system traces for verifying
21+
* the effectiveness of your Baseline Profiles. You can run it directly from Android
22+
* Studio as an instrumentation test, or run all benchmarks with this Gradle task:
23+
* ```
24+
* ./gradlew :baselineprofile:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=Macrobenchmark
25+
* ```
26+
*
27+
* You should run the benchmarks on a physical device, not an Android emulator, because the
28+
* emulator doesn't represent real world performance and shares system resources with its host.
29+
*
30+
* For more information, see the [Macrobenchmark documentation](https://d.android.com/macrobenchmark#create-macrobenchmark)
31+
* and the [instrumentation arguments documentation](https://d.android.com/topic/performance/benchmarking/macrobenchmark-instrumentation-args).
32+
**/
33+
@RunWith(AndroidJUnit4::class)
34+
@LargeTest
35+
class StartupBenchmarks {
36+
37+
@get:Rule
38+
val rule = MacrobenchmarkRule()
39+
40+
@Test
41+
fun startupCompilationNone() =
42+
benchmark(CompilationMode.None())
43+
44+
@Test
45+
fun startupCompilationBaselineProfiles() =
46+
benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
47+
48+
private fun benchmark(compilationMode: CompilationMode) {
49+
rule.measureRepeated(
50+
packageName = PACKAGE_NAME,
51+
metrics = listOf(StartupTimingMetric()),
52+
compilationMode = compilationMode,
53+
startupMode = StartupMode.COLD,
54+
iterations = 10,
55+
setupBlock = {
56+
pressHome()
57+
},
58+
measureBlock = {
59+
startActivityAndWait()
60+
61+
// TODO Add interactions to wait for when your app is fully drawn.
62+
// The app is fully drawn when Activity.reportFullyDrawn is called.
63+
// For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
64+
// from the AndroidX Activity library.
65+
66+
// Check the UiAutomator documentation for more information on how to
67+
// interact with the app.
68+
// https://d.android.com/training/testing/other-components/ui-automator
69+
}
70+
)
71+
}
72+
}

build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ plugins {
55
alias(libs.plugins.androidGradleLibraryPlugin) apply false
66
alias(libs.plugins.kotlinGradlePlugin) apply false
77
alias(libs.plugins.kotlinxKover) apply false
8+
alias(libs.plugins.androidTest) apply false
9+
alias(libs.plugins.androidx.baselineprofile) apply false
810
}
911

1012
tasks.register("clean", Delete::class) {
@@ -19,4 +21,4 @@ buildscript {
1921
}
2022
}
2123
}
22-
}
24+
}

gradle/libs.versions.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ detekt = "1.23.1"
3838
twitterComposeRule = "0.0.26"
3939
lottie = "6.1.0"
4040
kover = "0.7.3"
41+
uiautomator = "2.2.0"
42+
benchmark-macro-junit4 = "1.2.0-beta01"
43+
androidx-baselineprofile = "1.2.0-beta01"
44+
profileinstaller = "1.3.1"
4145

4246
[libraries]
4347
androidGradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
@@ -141,6 +145,9 @@ roborazziCompose = { module = "io.github.takahirom.roborazzi:roborazzi-compose",
141145
roborazziRule = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi" }
142146
showkaseRuntime = { group = "com.airbnb.android", name = "showkase", version.ref = "showkase" }
143147
showkaseProcessor = { group = "com.airbnb.android", name = "showkase-processor", version.ref = "showkase" }
148+
uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" }
149+
benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmark-macro-junit4" }
150+
profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "profileinstaller" }
144151

145152
[plugins]
146153
androidGradlePlugin = { id = "com.android.application", version.ref = "androidGradlePlugin" }
@@ -152,6 +159,8 @@ kspGradlePlugin = { id = "com.google.devtools.ksp", version.ref = "ksp" }
152159
kotlinxKover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
153160
detektGradlePlugin = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
154161
ossLicensesPlugin = { id = "com.google.android.gms.oss-licenses-plugin", version.ref = "ossLicensesPlugin" }
162+
androidTest = { id = "com.android.test", version.ref = "androidGradlePlugin" }
163+
androidx-baselineprofile = { id = "androidx.baselineprofile", version.ref = "androidx-baselineprofile" }
155164

156165
[bundles]
157166
plugins = [

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ include(
3333
":core:testing",
3434
":core:common",
3535
)
36+
include(":baselineprofile")

0 commit comments

Comments
 (0)