├── CODEOWNERS
├── JankStatsSample
├── app
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── themes.xml
│ │ │ │ └── strings.xml
│ │ │ ├── drawable
│ │ │ │ ├── ic_views.xml
│ │ │ │ ├── ic_construction.xml
│ │ │ │ └── ic_compose.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ ├── menu
│ │ │ │ └── bottom_menu.xml
│ │ │ ├── layout
│ │ │ │ ├── message_item.xml
│ │ │ │ ├── fragment_message_list.xml
│ │ │ │ ├── fragment_message_content.xml
│ │ │ │ └── activity_jank_logging.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── navigation
│ │ │ │ └── nav_graph.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── jankstats
│ │ │ │ ├── compose
│ │ │ │ └── rememberMetricsStateHolder.kt
│ │ │ │ ├── JankyView.kt
│ │ │ │ ├── tools
│ │ │ │ └── simulateJank.kt
│ │ │ │ ├── MessageContentFragment.kt
│ │ │ │ └── MessageListAdapter.kt
│ │ │ └── AndroidManifest.xml
│ └── build.gradle.kts
├── gradle
│ ├── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ └── libs.versions.toml
├── .gitignore
├── build.gradle
├── settings.gradle.kts
├── gradle.properties
└── README.md
├── MacrobenchmarkSample
├── app
│ ├── .gitignore
│ ├── benchmark-rules.txt
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable
│ │ │ │ ├── ic_star.xml
│ │ │ │ ├── ic_comment.xml
│ │ │ │ ├── ic_more.xml
│ │ │ │ ├── ic_thumb_up.xml
│ │ │ │ └── ic_thumb_down.xml
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── themes.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_nested_rv.xml
│ │ │ │ ├── activity_list_view.xml
│ │ │ │ ├── activity_recycler_view.xml
│ │ │ │ ├── activity_scroll_view.xml
│ │ │ │ ├── recycler_row.xml
│ │ │ │ └── item_parent.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── kotlin
│ │ │ └── com
│ │ │ └── example
│ │ │ └── macrobenchmark
│ │ │ └── target
│ │ │ ├── util
│ │ │ ├── AppLogin.kt
│ │ │ ├── Data.kt
│ │ │ ├── Utils.kt
│ │ │ └── SampleViewModel.kt
│ │ │ ├── activity
│ │ │ ├── clicklatency
│ │ │ │ ├── NonExportedRecyclerActivity.kt
│ │ │ │ ├── RecyclerViewActivity.kt
│ │ │ │ └── NestedRecyclerActivity.kt
│ │ │ └── FullyDrawnStartupActivity.kt
│ │ │ ├── recyclerview
│ │ │ ├── BindingViewHolder.kt
│ │ │ ├── ParentItem.kt
│ │ │ ├── ChildItem.kt
│ │ │ ├── Entry.kt
│ │ │ └── NestedRecyclerViewModel.kt
│ │ │ └── ExampleApplication.kt
│ └── keep-rules.pro
├── functions
│ ├── .nvmrc
│ ├── src
│ │ ├── flags.ts
│ │ ├── index.ts
│ │ ├── logger.ts
│ │ └── schema.ts
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── README.md
│ ├── package.json
│ └── samples
│ │ ├── sample_benchmark_result_2.json
│ │ └── sample_benchmark_result_1.json
├── macrobenchmark
│ ├── .gitignore
│ └── src
│ │ └── main
│ │ ├── kotlin
│ │ └── com
│ │ │ └── example
│ │ │ └── macrobenchmark
│ │ │ ├── benchmark
│ │ │ ├── util
│ │ │ │ └── Utils.kt
│ │ │ ├── startup
│ │ │ │ ├── SampleStartupBenchmark.kt
│ │ │ │ ├── FullyDrawnStartupBenchmark.kt
│ │ │ │ └── SmallListStartupBenchmark.kt
│ │ │ ├── frames
│ │ │ │ └── TextInputFrameTimingBenchmark.kt
│ │ │ └── scroll
│ │ │ │ └── ScrollBenchmark.kt
│ │ │ └── baselineprofile
│ │ │ ├── StartupProfileGenerator.kt
│ │ │ ├── LoginBaselineProfileGenerator.kt
│ │ │ └── SampleBaselineProfileGenerator.kt
│ │ └── AndroidManifest.xml
├── .firebaserc
├── firebase.json
├── ftl
│ └── images
│ │ ├── monitoring.png
│ │ ├── github_actions.png
│ │ ├── cloud_function_settings.png
│ │ ├── custom_startupMs_maximum.png
│ │ ├── cloud_function_invocation.png
│ │ └── cloud_function_configuration.png
├── media
│ └── composition-tracing.png
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── .gitignore
├── build.gradle.kts
├── settings.gradle.kts
├── com.example.macrobenchmark-benchmarkData.json
└── gradle.properties
├── MicrobenchmarkSample
├── app
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── themes.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── AndroidManifest.xml
│ ├── proguard-rules.pro
│ └── build.gradle.kts
├── benchmarkable
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── drawable-nodpi
│ │ │ │ └── singapore.jpg
│ │ │ ├── values
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── colors.xml
│ │ │ ├── drawable
│ │ │ │ ├── shape_oval.xml
│ │ │ │ ├── info_background.xml
│ │ │ │ └── ic_star.xml
│ │ │ └── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_view.xml
│ │ │ │ └── item_card.xml
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── benchmark
│ │ │ ├── ui
│ │ │ ├── Holder.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── SampleAdapter.kt
│ │ │ └── SortingAlgorithms.kt
│ │ │ └── data
│ │ │ └── SomeWork.kt
│ ├── proguard-rules.pro
│ └── build.gradle.kts
├── gradle
│ ├── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ └── libs.versions.toml
├── .gitignore
├── microbenchmark
│ ├── src
│ │ ├── androidTest
│ │ │ ├── assets
│ │ │ │ └── images
│ │ │ │ │ └── jetpack.png
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── benchmark
│ │ │ │ ├── SampleBenchmark.kt
│ │ │ │ ├── ViewInflateBenchmark.kt
│ │ │ │ ├── AutoBoxingBenchmark.kt
│ │ │ │ ├── TextBenchmarkUtils.kt
│ │ │ │ ├── BitmapBenchmark.kt
│ │ │ │ ├── SortingBenchmarks.kt
│ │ │ │ └── ViewMeasureLayoutBenchmark.kt
│ │ └── main
│ │ │ └── AndroidManifest.xml
│ └── build.gradle.kts
├── build.gradle.kts
├── settings.gradle.kts
└── gradle.properties
├── .github
├── CODEOWNERS
├── blunderbuss.yml
├── stale.yml
├── ci-gradle.properties
└── workflows
│ └── macrobenchmark.yml
├── renovate.json
├── .gitmodules
├── pull_traces.sh
├── pull_benchmark_data.sh
├── .gitignore
├── CONTRIBUTING.md
└── README.md
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @mlykotom @keyboardsurfer
--------------------------------------------------------------------------------
/JankStatsSample/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/.nvmrc:
--------------------------------------------------------------------------------
1 | 22.17.1
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @android/performance-devrel
2 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/.github/blunderbuss.yml:
--------------------------------------------------------------------------------
1 | assign_issues:
2 | - android/performance-devrel
3 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/src/flags.ts:
--------------------------------------------------------------------------------
1 | /* Logging turned on ? */
2 | export const IS_LOG = true;
3 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/benchmark-rules.txt:
--------------------------------------------------------------------------------
1 | # Benchmark builds should not be obfuscated.
2 | -dontobfuscate
3 |
4 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "macrobenchmark-samples-6670d"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "functions": {
3 | "predeploy": "npm --prefix \"$RESOURCE_DIR\" run build"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/ftl/images/monitoring.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/ftl/images/monitoring.png
--------------------------------------------------------------------------------
/JankStatsSample/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/MacrobenchmarkSample/ftl/images/github_actions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/ftl/images/github_actions.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/media/composition-tracing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/media/composition-tracing.png
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Example Microbenchmark Application
3 |
4 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/MicrobenchmarkSample/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "local>android/.github:renovate-config"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/ftl/images/cloud_function_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/ftl/images/cloud_function_settings.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/ftl/images/custom_startupMs_maximum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/ftl/images/custom_startupMs_maximum.png
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/MacrobenchmarkSample/ftl/images/cloud_function_invocation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/ftl/images/cloud_function_invocation.png
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/ftl/images/cloud_function_configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/ftl/images/cloud_function_configuration.png
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/JankStatsSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MacrobenchmarkSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "BenchmarkNdkSample/benchmark/cpp/third_party/googlebenchmark"]
2 | path = BenchmarkNdkSample/benchmark/cpp/third_party/googlebenchmark
3 | url = https://github.com/google/benchmark.git
4 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | .gradle
4 | /local.properties
5 | .DS_Store
6 | build/
7 | /captures
8 |
9 | # JNI Native build artifacts
10 | **/.externalNativeBuild
11 | **/.cxx
12 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/drawable-nodpi/singapore.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/benchmarkable/src/main/res/drawable-nodpi/singapore.jpg
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/assets/images/jetpack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android/performance-samples/HEAD/MicrobenchmarkSample/microbenchmark/src/androidTest/assets/images/jetpack.png
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled JavaScript files
2 | lib/**/*.js
3 | lib/**/*.js.map
4 |
5 | # TypeScript v1 declaration files
6 | typings/
7 |
8 | # Node.js dependency directory
9 | node_modules/
10 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/keep-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific Keep Rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/r8
--------------------------------------------------------------------------------
/pull_traces.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # The following command pulls all files ending in .perfetto-trace from the directory
4 | # hierarchy starting at the root /storage/emulated/0/Android.
5 | adb shell find /storage/emulated/0/Android/ -name "*.perfetto-trace" \
6 | | tr -d '\r' | xargs -n1 adb pull
--------------------------------------------------------------------------------
/pull_benchmark_data.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # The following command pulls all files ending in -benchmarkData.json from the directory
4 | # hierarchy starting at the root /storage/emulated/0/Android.
5 | adb shell find /storage/emulated/0/Android/ -name "*-benchmarkData.json" \
6 | | tr -d '\r' | xargs -n1 adb pull
--------------------------------------------------------------------------------
/JankStatsSample/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/JankStatsSample/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "noImplicitReturns": true,
5 | "outDir": "lib",
6 | "sourceMap": true,
7 | "strict": true,
8 | "target": "es2017"
9 | },
10 | "compileOnSave": true,
11 | "include": [
12 | "src"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as F from "firebase-functions";
2 |
3 | import { firebaseTestLabHandler, firebaseTestLabHttpsHandler } from './handlers';
4 |
5 | // Register handlers
6 | export const completionHandler = F.testLab.testMatrix().onComplete(firebaseTestLabHandler);
7 | export const httpsHandler = F.https.onRequest(firebaseTestLabHttpsHandler);
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | .gradle
4 | /local.properties
5 | .DS_Store
6 | build/
7 | /captures
8 | benchmark-repository
9 |
10 | # JNI Native build artifacts
11 | **/.externalNativeBuild
12 | **/.cxx
13 |
14 | # Ignores output from the ConstraintLayout performance comparison sample.
15 | **/*RunCalculationTraditionalLayouts.html
16 | **/*RunCalculationConstraintLayout.html
17 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | plugins {
4 | alias(libs.plugins.application) apply false
5 | alias(libs.plugins.benchmark) apply false
6 | alias(libs.plugins.library) apply false
7 | alias(libs.plugins.test) apply false
8 | alias(libs.plugins.kotlin) apply false
9 | }
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/src/logger.ts:
--------------------------------------------------------------------------------
1 | import {IS_LOG} from './flags';
2 |
3 | const TAG = "MacroBenchmarks"
4 |
5 | export function log(message: string, ...args: any[]) {
6 | if (IS_LOG) {
7 | let length = args ? args.length : 0;
8 | if (length > 0) {
9 | console.log(TAG, message, ...args);
10 | } else {
11 | console.log(TAG, message);
12 | }
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
11 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/README.md:
--------------------------------------------------------------------------------
1 | # Cloud Functions for Macrobenchmarks.
2 |
3 | ## Initialize Project
4 |
5 | ```bash
6 | gcloud config set project macrobenchmark-samples-6670d
7 | ```
8 |
9 | ## Deploy Functions
10 |
11 | ```bash
12 | firebase deploy --only functions
13 | ```
14 |
15 | ### Setup Environment variables
16 |
17 | Example:
18 |
19 | ```bash
20 | # Example: Setup Environment Variables.
21 | package_name="com.example.macrobenchmark.target"
22 | device_configurations='["flame-30-en-portrait"]'
23 | ```
24 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/drawable/ic_views.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/java/com/example/jankstats/compose/rememberMetricsStateHolder.kt:
--------------------------------------------------------------------------------
1 | package com.example.jankstats.compose
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.remember
5 | import androidx.compose.ui.platform.LocalView
6 | import androidx.metrics.performance.PerformanceMetricsState
7 |
8 | // [START metrics_state_holder]
9 | /**
10 | * Retrieve MetricsStateHolder from compose and remember until the current view changes.
11 | */
12 | @Composable
13 | fun rememberMetricsStateHolder(): PerformanceMetricsState.Holder {
14 | val view = LocalView.current
15 | return remember(view) { PerformanceMetricsState.getHolderForHierarchy(view) }
16 | }
17 | // [END metrics_state_holder]
18 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: wontfix
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/java/com/example/benchmark/ui/Holder.kt:
--------------------------------------------------------------------------------
1 | package com.example.benchmark.ui
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.TextView
7 | import androidx.recyclerview.widget.RecyclerView
8 |
9 | class Holder(root: View) : RecyclerView.ViewHolder(root) {
10 | private val textView: TextView = root.findViewById(R.id.label)
11 |
12 | fun bind(string: String) {
13 | textView.text = string
14 | }
15 |
16 | companion object {
17 | fun create(parent: ViewGroup): Holder {
18 | val inflater = LayoutInflater.from(parent.context)
19 | return Holder(inflater.inflate(R.layout.item_view, parent, false))
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloud-functions",
3 | "scripts": {
4 | "build": "tsc",
5 | "watch": "tsc --watch",
6 | "serve": "npm run build && firebase emulators:start --only functions",
7 | "shell": "npm run build && firebase functions:shell",
8 | "start": "npm run shell",
9 | "deploy": "firebase deploy --only functions",
10 | "logs": "firebase functions:log"
11 | },
12 | "engines": {
13 | "node": "22.17.1"
14 | },
15 | "main": "lib/index.js",
16 | "dependencies": {
17 | "@google-cloud/monitoring": "^5.0.0",
18 | "@google-cloud/storage": "^7.13.0",
19 | "firebase-admin": "^13.0.0",
20 | "firebase-functions": "^6.0.1"
21 | },
22 | "devDependencies": {
23 | "firebase-functions-test": "^3.3.0",
24 | "typescript": "^5.6.2"
25 | },
26 | "private": true
27 | }
28 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/drawable/ic_construction.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/JankStatsSample/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.application) apply false
19 | alias(libs.plugins.kotlin) apply false
20 | alias(libs.plugins.compose.compiler) apply false
21 |
22 | }
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 8dp
20 |
21 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/util/AppLogin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.util
18 |
19 | data class AppLogin(var userName: String = "", var password: String = "")
20 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/java/com/example/benchmark/data/SomeWork.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.benchmark.data
18 |
19 | fun doSomeWork() {
20 | // Pretend this method does some work
21 | Thread.sleep(100)
22 | }
23 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/benchmark/util/Utils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.benchmark.util
18 |
19 | const val TARGET_PACKAGE = "com.example.macrobenchmark.target"
20 | const val DEFAULT_ITERATIONS = 1
21 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/drawable/shape_oval.xml:
--------------------------------------------------------------------------------
1 |
16 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.application) apply false
19 | alias(libs.plugins.library) apply false
20 | alias(libs.plugins.test) apply false
21 | alias(libs.plugins.kotlin) apply false
22 | alias(libs.plugins.baselineprofile) apply false
23 | }
24 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.library")
3 | id("kotlin-android")
4 | }
5 |
6 | android {
7 | compileSdk = 35
8 | namespace = "com.example.benchmark.ui"
9 |
10 | defaultConfig {
11 | minSdk = 21
12 | }
13 |
14 | compileOptions {
15 | sourceCompatibility = JavaVersion.VERSION_18
16 | targetCompatibility = JavaVersion.VERSION_18
17 | }
18 |
19 | kotlinOptions {
20 | jvmTarget = JavaVersion.VERSION_18.toString()
21 | }
22 | }
23 |
24 | dependencies {
25 |
26 | implementation(libs.appcompat)
27 | implementation(libs.cardview)
28 | implementation(libs.constraintlayout)
29 | implementation(libs.kotlin.stdlib)
30 | implementation(libs.recyclerview)
31 |
32 | androidTestImplementation(libs.espresso)
33 | androidTestImplementation(libs.test.ext)
34 | androidTestImplementation(libs.test.runner)
35 |
36 | testImplementation(libs.junit)
37 | }
38 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/drawable/ic_compose.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/drawable/info_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | -
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/java/com/example/benchmark/ui/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.benchmark.ui
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 | class MainActivity : AppCompatActivity() {
8 |
9 | // It's used in benchmarks
10 | @Suppress("MemberVisibilityCanBePrivate")
11 | val adapter = SampleAdapter()
12 |
13 | lateinit var recyclerView: RecyclerView
14 |
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 |
18 | setContentView(R.layout.activity_main)
19 |
20 | recyclerView = findViewById(R.id.recyclerview)
21 | recyclerView.adapter = adapter
22 | adapter.submitList(
23 | mutableListOf(
24 | "Welcome",
25 | "to",
26 | "this",
27 | "Microbenchmark",
28 | "sample",
29 | )
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/activity/clicklatency/NonExportedRecyclerActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.activity.clicklatency
18 |
19 | /**
20 | * The same as a [RecyclerViewActivity], but not `exported` via the `AndroidManifest.xml`.
21 | */
22 | class NonExportedRecyclerActivity : RecyclerViewActivity()
23 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/JankStatsSample/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | rootProject.name = "JankStats Sample"
18 | include(":app")
19 |
20 | pluginManagement {
21 | repositories {
22 | google()
23 | mavenCentral()
24 | gradlePluginPortal()
25 | }
26 | }
27 | dependencyResolutionManagement {
28 | repositories {
29 | google()
30 | mavenCentral()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/recyclerview/BindingViewHolder.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.recyclerview
18 |
19 | import androidx.recyclerview.widget.RecyclerView
20 | import androidx.viewbinding.ViewBinding
21 |
22 | class BindingViewHolder(
23 | val binding: T
24 | ) : RecyclerView.ViewHolder(binding.root)
25 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/ExampleApplication.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target
18 |
19 | import android.app.Application
20 | import com.example.macrobenchmark.target.util.ClickTrace
21 |
22 | class ExampleApplication : Application() {
23 |
24 | override fun onCreate() {
25 | super.onCreate()
26 | ClickTrace.install()
27 | }
28 | }
--------------------------------------------------------------------------------
/.github/ci-gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2022 The Android Open Source Project
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | org.gradle.daemon=false
18 | org.gradle.parallel=true
19 | org.gradle.workers.max=2
20 |
21 | kotlin.incremental=false
22 | kotlin.compiler.execution.strategy=in-process
23 |
24 | # Controls KotlinOptions.allWarningsAsErrors.
25 | # This value used in CI and is currently set to false.
26 | # If you want to treat warnings as errors locally, set this property to true
27 | # in your ~/.gradle/gradle.properties file.
28 | warningsAsErrors=false
--------------------------------------------------------------------------------
/MacrobenchmarkSample/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | rootProject.name = "macrobenchmark"
18 | include(":app")
19 | include(":macrobenchmark")
20 |
21 |
22 | pluginManagement {
23 | repositories {
24 | google()
25 | mavenCentral()
26 | gradlePluginPortal()
27 | }
28 | }
29 | dependencyResolutionManagement {
30 | repositories {
31 | google()
32 | mavenCentral()
33 | gradlePluginPortal()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/drawable/ic_star.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | rootProject.name = "microbenchmark"
18 | include(":app")
19 | include(":benchmarkable")
20 | include(":microbenchmark")
21 |
22 | pluginManagement {
23 | repositories {
24 | google()
25 | mavenCentral()
26 | gradlePluginPortal()
27 | }
28 | }
29 |
30 | dependencyResolutionManagement {
31 | repositories {
32 | google()
33 | mavenCentral()
34 | gradlePluginPortal()
35 | }
36 | }
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | #FFBB86FC
20 | #FF6200EE
21 | #FF3700B3
22 | #FF03DAC5
23 | #FF018786
24 | #FF000000
25 | #FFFFFFFF
26 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/drawable/ic_star.xml:
--------------------------------------------------------------------------------
1 |
16 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Performance Test App
3 |
4 | Singapore officially the Republic of Singapore, and often referred to as the Lion City, the Garden City, and the Red Dot, is a global city in Southeast Asia and the world\'s only island city-state. It lies one degree (137 km) north of the equator, at the southernmost tip of continental Asia and peninsular Malaysia, with Indonesia\'s Riau Islands to the south. Singapore\'s territory consists of the diamond-shaped main island and 62 islets.
5 | dummy
6 | Discard
7 | Upload
8 | ƒ/4 16s ISO 200
9 | Settings
10 | Camera
11 | Leica M Typ 240
12 | Singapore
13 |
14 | Mark
15 | Benchmarking is awesome
16 |
17 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/drawable/ic_comment.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/samples/sample_benchmark_result_2.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "build": {
4 | "brand": "google",
5 | "device": "redfin",
6 | "fingerprint": "google/redfin/redfin:S/SP1A.210202.002/7117294:userdebug/dev-keys",
7 | "model": "Pixel 5",
8 | "version": {
9 | "sdk": 30
10 | }
11 | },
12 | "cpuCoreCount": 8,
13 | "cpuLocked": false,
14 | "cpuMaxFreqHz": 2400000000,
15 | "memTotalBytes": 7818850304,
16 | "sustainedPerformanceModeEnabled": false
17 | },
18 | "benchmarks": [
19 | {
20 | "name": "startup[mode=COLD]",
21 | "params": {
22 | "mode": "COLD"
23 | },
24 | "className": "com.example.macrobenchmark.TrivialStartupBenchmark",
25 | "totalRunTimeNs": 27999341747,
26 | "metrics": {
27 | "startupMs": {
28 | "minimum": 195,
29 | "maximum": 210,
30 | "median": 196,
31 | "runs": [
32 | 210,
33 | 196,
34 | 195
35 | ]
36 | }
37 | },
38 | "warmupIterations": 0,
39 | "repeatIterations": 3,
40 | "thermalThrottleSleepSeconds": 0
41 | }
42 | ]
43 | }
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows
28 | [Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).
29 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/drawable/ic_more.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/java/com/example/benchmark/ui/SampleAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.example.benchmark.ui
2 |
3 | import android.view.ViewGroup
4 | import androidx.recyclerview.widget.DiffUtil
5 | import androidx.recyclerview.widget.ListAdapter
6 |
7 | class SampleAdapter : ListAdapter(StringDiffCallback) {
8 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
9 | return Holder.create(parent)
10 | }
11 |
12 | override fun onBindViewHolder(holder: Holder, position: Int) {
13 | holder.bind(getItem(position))
14 | }
15 | }
16 |
17 | private object StringDiffCallback : DiffUtil.ItemCallback() {
18 | /**
19 | * In real diff callback, you'd want to return true if two objects refer to the same thing.
20 | * It may be some ID or a field uniquely identifying the thing.
21 | *
22 | * In this sample, we don't have changing list item, so simply using equality.
23 | */
24 | override fun areItemsTheSame(oldItem: String, newItem: String): Boolean =
25 | oldItem == newItem
26 |
27 | override fun areContentsTheSame(oldItem: String, newItem: String): Boolean =
28 | oldItem == newItem
29 | }
30 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 | #5d00ed
18 | #9946ff
19 | #0000b9
20 | #4caf50
21 | #80e27e
22 | #087f23
23 | #ffffff
24 | #000000
25 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/JankStatsSample/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/drawable/ic_thumb_up.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/util/Data.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.util
18 |
19 | import kotlinx.coroutines.Dispatchers
20 | import kotlinx.coroutines.delay
21 | import kotlinx.coroutines.withContext
22 | import java.util.Random
23 |
24 | /** Pretending to load data from a remote source */
25 | class SampleData {
26 | private val random = Random()
27 | suspend fun isReady(): Boolean {
28 | withContext(Dispatchers.Default) {
29 | delay(1000 + random.nextInt(1000).toLong())
30 | }
31 | return true
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/drawable/ic_thumb_down.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/menu/bottom_menu.xml:
--------------------------------------------------------------------------------
1 |
16 |
33 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("kotlin-android")
4 | }
5 |
6 | android {
7 | namespace = "com.example.app"
8 | compileSdk = 35
9 |
10 | defaultConfig {
11 | applicationId = "com.example.app"
12 | minSdk = 21
13 | targetSdk = 35
14 | versionCode = 1
15 | versionName = "1.0"
16 |
17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | getByName("release") {
22 | isMinifyEnabled = true
23 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility = JavaVersion.VERSION_18
28 | targetCompatibility = JavaVersion.VERSION_18
29 | }
30 | kotlinOptions {
31 | jvmTarget = JavaVersion.VERSION_18.toString()
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation(project(":benchmarkable"))
37 |
38 | implementation(libs.appcompat)
39 | implementation(libs.core)
40 | implementation(libs.material)
41 |
42 | androidTestImplementation(libs.espresso)
43 | androidTestImplementation(libs.test.ext)
44 |
45 | testImplementation(libs.junit)
46 | }
47 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/layout/activity_nested_rv.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
23 |
31 |
32 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/com.example.macrobenchmark-benchmarkData.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "build": {
4 | "brand": "google",
5 | "device": "blueline",
6 | "fingerprint": "google/blueline/blueline:12/SP1A.210812.015/7679548:user/release-keys",
7 | "model": "Pixel 3",
8 | "version": {
9 | "sdk": 31
10 | }
11 | },
12 | "cpuCoreCount": 8,
13 | "cpuLocked": false,
14 | "cpuMaxFreqHz": 2803200000,
15 | "memTotalBytes": 3753299968,
16 | "sustainedPerformanceModeEnabled": false
17 | },
18 | "benchmarks": [
19 | {
20 | "name": "startup",
21 | "params": {},
22 | "className": "com.example.macrobenchmark.startup.SampleStartupBenchmark",
23 | "totalRunTimeNs": 4975598256,
24 | "metrics": {
25 | "timeToInitialDisplayMs": {
26 | "minimum": 347.881076,
27 | "maximum": 347.881076,
28 | "median": 347.881076,
29 | "runs": [
30 | 347.881076
31 | ]
32 | }
33 | },
34 | "sampledMetrics": {},
35 | "warmupIterations": 0,
36 | "repeatIterations": 3,
37 | "thermalThrottleSleepSeconds": 0
38 | }
39 | ]
40 | }
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
23 |
28 |
29 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/java/com/example/benchmark/SampleBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.benchmark
18 |
19 | import androidx.benchmark.junit4.BenchmarkRule
20 | import androidx.benchmark.junit4.measureRepeated
21 | import androidx.test.ext.junit.runners.AndroidJUnit4
22 | import com.example.benchmark.data.doSomeWork
23 | import org.junit.Rule
24 | import org.junit.Test
25 | import org.junit.runner.RunWith
26 |
27 | // [START sample_benchmark]
28 | @RunWith(AndroidJUnit4::class)
29 | class SampleBenchmark {
30 | @get:Rule
31 | val benchmarkRule = BenchmarkRule()
32 |
33 | @Test
34 | fun benchmarkSomeWork() {
35 | benchmarkRule.measureRepeated {
36 | doSomeWork()
37 | }
38 | }
39 | }
40 | // [END sample_benchmark]
41 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/java/com/example/jankstats/JankyView.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.jankstats
18 |
19 | import android.content.Context
20 | import android.graphics.Canvas
21 | import android.util.AttributeSet
22 | import android.view.View
23 | import com.example.jankstats.tools.simulateJank
24 |
25 | /**
26 | * This custom view is used to inject an artificial, random delay during drawing, to simulate
27 | * jank on the UI thread.
28 | */
29 | class JankyView @JvmOverloads constructor(
30 | context: Context,
31 | attrs: AttributeSet? = null,
32 | defStyleAttr: Int = 0,
33 | defStyleRes: Int = 0
34 | ) : View(context, attrs, defStyleAttr, defStyleRes) {
35 |
36 | override fun onDraw(canvas: Canvas) {
37 | simulateJank()
38 | super.onDraw(canvas)
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/src/schema.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The Macrobenchmark Context.
3 | */
4 | export type BenchmarkContext = {
5 | "build": {
6 | 'brand': string,
7 | 'device': string,
8 | 'fingerprint': string,
9 | 'model': string,
10 | 'version': {
11 | "sdk": number
12 | }
13 | },
14 | "cpuCoreCount": number,
15 | "cpuLocked": boolean,
16 | "cpuMaxFreqHz": number,
17 | "memTotalBytes": number,
18 | "sustainedPerformanceModeEnabled": boolean
19 | };
20 |
21 | /**
22 | * The Metrics Payload.
23 | */
24 | export type Metrics = {
25 | 'minimum': number;
26 | 'maximum': number;
27 | 'median': number;
28 | 'runs': Array;
29 | };
30 |
31 | /**
32 | * The Benchmark.
33 | */
34 | export type Benchmark = {
35 | 'name': string;
36 | 'params': object;
37 | 'className': string;
38 | 'totalRunTimeNs': number;
39 | 'metrics': {
40 | // Startup Timing Metrics
41 | 'startupMs'?: Metrics;
42 | // Frame Timing Metrics
43 | 'frameTime50thPercentileMs'?: Metrics;
44 | 'frameTime90thPercentileMs'?: Metrics;
45 | 'frameTime95thPercentileMs'?: Metrics;
46 | 'frameTime99thPercentileMs'?: Metrics;
47 | 'totalFrameCount'?: Metrics;
48 | }
49 | 'warmupIterations': number;
50 | 'repeatIterations': number;
51 | 'thermalThrottleSleepSeconds': number;
52 | };
53 |
54 | /**
55 | * The Benchmarks result.
56 | */
57 | export type Benchmarks = {
58 | 'context': BenchmarkContext
59 | 'benchmarks': Array
60 | };
61 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Macrobenchmark Target App.
19 | A list of items
20 | Item options
21 | Login
22 | Fully Drawn
23 | RecyclerView
24 | ListView
25 | ScrollView
26 | Compose LazyList
27 | Nested RecyclerView
28 | Nested RecyclerView With Pools
29 |
30 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/util/Utils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.util
18 |
19 | import android.content.Context
20 | import android.util.TypedValue
21 | import androidx.datastore.core.DataStore
22 | import androidx.datastore.preferences.core.Preferences
23 | import androidx.datastore.preferences.core.stringPreferencesKey
24 | import androidx.datastore.preferences.preferencesDataStore
25 |
26 | const val TAG = "Perf Sample"
27 |
28 | val KEY_USER = stringPreferencesKey("user")
29 | val KEY_PASSWORD = stringPreferencesKey("password")
30 |
31 | val Context.dataStore: DataStore by preferencesDataStore(name = "settings")
32 |
33 | fun Int.dp(context: Context): Float =
34 | TypedValue.applyDimension(
35 | TypedValue.COMPLEX_UNIT_DIP,
36 | this.toFloat(),
37 | context.resources.displayMetrics
38 | )
39 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | JankStats
19 | Settings
20 |
21 | First Fragment
22 | Second Fragment
23 | Next
24 | Previous
25 |
26 | Hello first fragment
27 | Hello second fragment. Arg: %1$s
28 |
29 | Hello blank fragment
30 |
31 | Views
32 | Compose
33 | Not Implemented
34 |
35 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/layout/message_item.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
31 |
32 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/layout/item_view.xml:
--------------------------------------------------------------------------------
1 |
16 |
23 |
24 |
33 |
34 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
32 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/layout/activity_list_view.xml:
--------------------------------------------------------------------------------
1 |
16 |
21 |
22 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/layout/fragment_message_list.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
23 |
24 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/layout/activity_recycler_view.xml:
--------------------------------------------------------------------------------
1 |
16 |
21 |
22 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/recyclerview/ParentItem.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.recyclerview
18 |
19 | import androidx.recyclerview.widget.DiffUtil
20 |
21 | data class ParentItem(
22 | val id: String,
23 | val content: String,
24 | val children: List
25 | )
26 |
27 | object ParentItemDiffCallback : DiffUtil.ItemCallback() {
28 | override fun areItemsTheSame(oldItem: ParentItem, newItem: ParentItem): Boolean =
29 | oldItem.id == newItem.id
30 |
31 | override fun areContentsTheSame(oldItem: ParentItem, newItem: ParentItem): Boolean = when {
32 | oldItem.content != newItem.content -> false
33 | // go through the children and find whether are the same
34 | !oldItem.children
35 | .asSequence()
36 | .zip(newItem.children.asSequence())
37 | .all { (a, b) -> ChildItemDiffCallback.areContentsTheSame(a, b) } -> false
38 |
39 | else -> true
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/java/com/example/jankstats/tools/simulateJank.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.jankstats.tools
18 |
19 | import androidx.tracing.trace
20 | import kotlin.random.Random.Default.nextFloat
21 | import kotlin.random.Random.Default.nextLong
22 |
23 | /**
24 | * Inject random delay to cause jank in the app.
25 | * For any given item, there should be a 30% chance of jank (>32ms), and a 2% chance of
26 | * extreme jank (>500ms).
27 | * Regular jank will be between 32 and 82ms, extreme from 500-700ms.
28 | */
29 | fun simulateJank(
30 | jankProbability: Double = 0.3,
31 | extremeJankProbability: Double = 0.02
32 | ) {
33 | val probability = nextFloat()
34 |
35 | if (probability > 1 - jankProbability) {
36 | val delay = if (probability > 1 - extremeJankProbability) {
37 | nextLong(500, 700)
38 | } else {
39 | nextLong(32, 82)
40 | }
41 |
42 | try {
43 | // Make jank easier to spot in the profiler through tracing.
44 | trace("Jank Simulation") {
45 | Thread.sleep(delay)
46 | }
47 | } catch (e: Exception) {
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2022 The Android Open Source Project
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | # Project-wide Gradle settings.
18 | # IDE (e.g. Android Studio) users:
19 | # Gradle settings configured through the IDE *will override*
20 | # any settings specified in this file.
21 | # For more details on how to configure your build environment visit
22 | # http://www.gradle.org/docs/current/userguide/build_environment.html
23 | # Specifies the JVM arguments used for the daemon process.
24 | # The setting is particularly useful for tweaking memory settings.
25 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
26 | # When configured, Gradle will run in incubating parallel mode.
27 | # This option should only be used with decoupled projects. More details, visit
28 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
29 | # org.gradle.parallel=true
30 | # AndroidX package structure to make it clearer which packages are bundled with the
31 | # Android operating system, and which are packaged with your app"s APK
32 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
33 | android.useAndroidX=true
34 | # Kotlin code style for this project: "official" or "obsolete":
35 | kotlin.code.style=official
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/layout/activity_scroll_view.xml:
--------------------------------------------------------------------------------
1 |
16 |
22 |
23 |
33 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
32 |
33 |
37 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/java/com/example/benchmark/ViewInflateBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.benchmark
18 |
19 | import android.view.LayoutInflater
20 | import android.widget.FrameLayout
21 | import androidx.benchmark.junit4.BenchmarkRule
22 | import androidx.benchmark.junit4.measureRepeated
23 | import androidx.test.ext.junit.runners.AndroidJUnit4
24 | import androidx.test.platform.app.InstrumentationRegistry
25 | import com.example.benchmark.ui.R
26 | import org.junit.Rule
27 | import org.junit.Test
28 | import org.junit.runner.RunWith
29 |
30 | // [START simple_benchmark]
31 | @RunWith(AndroidJUnit4::class)
32 | class ViewInflateBenchmark {
33 |
34 | @get:Rule
35 | val benchmarkRule = BenchmarkRule()
36 |
37 | @Test
38 | fun benchmarkViewInflate() {
39 | val context = InstrumentationRegistry.getInstrumentation().context
40 | val inflater = LayoutInflater.from(context)
41 | val root = FrameLayout(context)
42 |
43 | benchmarkRule.measureRepeated {
44 | @Suppress("UNUSED_VARIABLE")
45 | val inflated = inflater.inflate(R.layout.item_card, root, false)
46 | }
47 | }
48 | }
49 | // [END simple_benchmark]
50 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/java/com/example/jankstats/MessageContentFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.jankstats
18 |
19 | import android.os.Bundle
20 | import android.view.LayoutInflater
21 | import android.view.View
22 | import android.view.ViewGroup
23 | import androidx.fragment.app.Fragment
24 | import com.example.jankstats.databinding.FragmentMessageContentBinding
25 |
26 | /**
27 | * A simple [Fragment] subclass.
28 | */
29 | class MessageContentFragment : Fragment() {
30 |
31 | private var _binding: FragmentMessageContentBinding? = null
32 | private val binding get() = requireNotNull(_binding)
33 |
34 | override fun onCreateView(
35 | inflater: LayoutInflater,
36 | container: ViewGroup?,
37 | savedInstanceState: Bundle?
38 | ): View {
39 | // Inflate the layout for this fragment
40 | _binding = FragmentMessageContentBinding.inflate(inflater)
41 | return binding.root
42 | }
43 |
44 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
45 | binding.messageHeader.text = arguments?.getString("title")
46 | }
47 |
48 | override fun onDestroyView() {
49 | _binding = null
50 | super.onDestroyView()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/baselineprofile/StartupProfileGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.baselineprofile
18 |
19 | import androidx.benchmark.macro.junit4.BaselineProfileRule
20 | import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
21 | import androidx.test.uiautomator.uiAutomator
22 | import com.example.macrobenchmark.benchmark.util.TARGET_PACKAGE
23 | import org.junit.Rule
24 | import org.junit.Test
25 | import org.junit.runner.RunWith
26 |
27 | /**
28 | * Generates a startup profile.
29 | * See
30 | * [the documentation](https://d.android.com//topic/performance/baselineprofiles/dex-layout-optimizations)
31 | * for details.
32 | */
33 | @RunWith(AndroidJUnit4ClassRunner::class)
34 | class StartupProfileGenerator {
35 | @get:Rule
36 | val rule = BaselineProfileRule()
37 |
38 | @Test
39 | fun profileGenerator() {
40 | rule.collect(
41 | packageName = TARGET_PACKAGE,
42 | maxIterations = 15,
43 | stableIterations = 3,
44 | includeInStartupProfile = true
45 | ) {
46 | uiAutomator {
47 | startApp(TARGET_PACKAGE)
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/benchmark/startup/SampleStartupBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.benchmark.startup
18 |
19 | import androidx.benchmark.macro.StartupTimingMetric
20 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
21 | import androidx.test.ext.junit.runners.AndroidJUnit4
22 | import androidx.test.filters.LargeTest
23 | import androidx.test.uiautomator.uiAutomator
24 | import com.example.macrobenchmark.benchmark.util.DEFAULT_ITERATIONS
25 | import com.example.macrobenchmark.benchmark.util.TARGET_PACKAGE
26 | import org.junit.Rule
27 | import org.junit.Test
28 | import org.junit.runner.RunWith
29 |
30 | // [START macrobenchmark_startup]
31 | @LargeTest
32 | @RunWith(AndroidJUnit4::class)
33 | class SampleStartupBenchmark {
34 | @get:Rule
35 | val benchmarkRule = MacrobenchmarkRule()
36 |
37 | @Test
38 | fun startup() = benchmarkRule.measureRepeated(
39 | packageName = TARGET_PACKAGE,
40 | metrics = listOf(StartupTimingMetric()),
41 | iterations = DEFAULT_ITERATIONS,
42 | ) {
43 | // starts default launch activity
44 | uiAutomator { startApp(TARGET_PACKAGE) }
45 | }
46 | }
47 | // [END macrobenchmark_startup]
48 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/recyclerview/ChildItem.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.recyclerview
18 |
19 | import androidx.recyclerview.widget.DiffUtil
20 |
21 | data class ChildItem(
22 | val id: String,
23 | val parentId: String,
24 | val title: String,
25 | val subtitle: String,
26 | val description: String,
27 | val color: Int,
28 | val inWishlist: Boolean = false,
29 | val likeState: LikeState = LikeState.UNKNOWN,
30 | )
31 |
32 | enum class LikeState {
33 | LIKED,
34 | DISLIKED,
35 | UNKNOWN
36 | }
37 |
38 | object ChildItemDiffCallback : DiffUtil.ItemCallback() {
39 | override fun areItemsTheSame(oldItem: ChildItem, newItem: ChildItem): Boolean =
40 | oldItem.id == newItem.id
41 |
42 | override fun areContentsTheSame(oldItem: ChildItem, newItem: ChildItem): Boolean = when {
43 | oldItem.color != newItem.color -> false
44 | oldItem.title != newItem.title -> false
45 | oldItem.subtitle != newItem.subtitle -> false
46 | oldItem.description != newItem.description -> false
47 | oldItem.inWishlist != newItem.inWishlist -> false
48 | oldItem.likeState != newItem.likeState -> false
49 | else -> true
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/baselineprofile/LoginBaselineProfileGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.baselineprofile
18 |
19 | import android.content.Intent
20 | import androidx.benchmark.macro.junit4.BaselineProfileRule
21 | import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
22 | import androidx.test.uiautomator.textAsString
23 | import androidx.test.uiautomator.uiAutomator
24 | import com.example.macrobenchmark.benchmark.util.TARGET_PACKAGE
25 | import org.junit.Rule
26 | import org.junit.Test
27 | import org.junit.runner.RunWith
28 |
29 | @RunWith(AndroidJUnit4ClassRunner::class)
30 | class LoginBaselineProfileGenerator {
31 |
32 | @get:Rule
33 | val rule = BaselineProfileRule()
34 |
35 | @Test
36 | fun generate() {
37 | rule.collect(
38 | packageName = TARGET_PACKAGE,
39 | maxIterations = 15,
40 | stableIterations = 3
41 | ) {
42 | uiAutomator {
43 | startIntent(Intent("$packageName.LOGIN_ACTIVITY"))
44 | onElement { isEditable and !isPassword }.text = "user"
45 | onElement { isEditable && isPassword }.text = "password"
46 | onElement { textAsString() == "Login" }.click()
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/layout/fragment_message_content.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
21 |
22 |
32 |
33 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.11.1"
3 | appcompat = "1.7.1"
4 | benchmark = "1.4.0"
5 | cardView = "1.0.0"
6 | constraintLayout = "2.2.1"
7 | core = "1.16.0"
8 | kotlin = "2.2.0"
9 | material = "1.12.0"
10 | recyclerView = "1.4.0"
11 |
12 | androidxTest = "1.7.0"
13 | androidxTestRules = "1.6.1"
14 | espressoCore = "3.7.0"
15 | jUnit = "4.13.2"
16 | testExt = "1.3.0"
17 |
18 | [libraries]
19 |
20 | appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
21 | benchmark = { group = "androidx.benchmark", name = "benchmark-junit4", version.ref = "benchmark" }
22 | cardview = { group = "androidx.cardview", name = "cardview", version.ref = "cardView" }
23 | constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintLayout" }
24 | core = { group = "androidx.core", name = "core-ktx", version.ref = "core" }
25 | espresso = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
26 | junit = { group = "junit", name = "junit", version.ref = "jUnit" }
27 | kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
28 | material = { group = "com.google.android.material", name = "material", version.ref = "material" }
29 | recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerView" }
30 | test-ext = { group = "androidx.test.ext", name = "junit", version.ref = "testExt" }
31 | test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTestRules" }
32 | test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTest" }
33 |
34 | [plugins]
35 |
36 | application = { id = "com.android.application", version.ref = "agp" }
37 | benchmark = { id = "androidx.benchmark", version.ref = "benchmark" }
38 | library = { id = "com.android.library", version.ref = "agp" }
39 | test = { id = "com.android.test", version.ref = "agp" }
40 | kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
41 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/layout/recycler_row.xml:
--------------------------------------------------------------------------------
1 |
16 |
23 |
27 |
28 |
34 |
38 |
39 |
43 |
44 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/java/com/example/benchmark/AutoBoxingBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.benchmark
18 |
19 | import androidx.benchmark.junit4.BenchmarkRule
20 | import androidx.benchmark.junit4.measureRepeated
21 | import androidx.test.ext.junit.runners.AndroidJUnit4
22 | import androidx.test.filters.LargeTest
23 | import org.junit.Rule
24 | import org.junit.Test
25 | import org.junit.runner.RunWith
26 |
27 | @LargeTest
28 | @RunWith(AndroidJUnit4::class)
29 | class AutoBoxingBenchmark {
30 |
31 | @get:Rule
32 | val benchmarkRule = BenchmarkRule()
33 |
34 | /**
35 | * Measure the cost of allocating a boxed integer that takes advantage of ART's cache.
36 | */
37 | @Test
38 | fun integerArtCacheAlloc() {
39 | var i = Integer(1000)
40 | benchmarkRule.measureRepeated {
41 | if (i < 100) {
42 | i = Integer(i.toInt() + 1)
43 | } else {
44 | i = Integer(0)
45 | }
46 | }
47 | }
48 |
49 | /**
50 | * Measure the cost of allocating a boxed integer that falls outside the range of ART's cache.
51 | */
52 | @Test
53 | fun integerAlloc() {
54 | var i = Integer(1000)
55 | benchmarkRule.measureRepeated {
56 | if (i < 1100) {
57 | i = Integer(i.toInt() + 1)
58 | } else {
59 | i = Integer(1000)
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/benchmark/startup/FullyDrawnStartupBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.benchmark.startup
18 |
19 | import android.content.Intent
20 | import androidx.benchmark.macro.StartupMode
21 | import androidx.benchmark.macro.StartupTimingMetric
22 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
23 | import androidx.test.ext.junit.runners.AndroidJUnit4
24 | import androidx.test.filters.LargeTest
25 | import androidx.test.uiautomator.textAsString
26 | import androidx.test.uiautomator.uiAutomator
27 | import com.example.macrobenchmark.benchmark.util.DEFAULT_ITERATIONS
28 | import com.example.macrobenchmark.benchmark.util.TARGET_PACKAGE
29 | import org.junit.Rule
30 | import org.junit.Test
31 | import org.junit.runner.RunWith
32 |
33 | @LargeTest
34 | @RunWith(AndroidJUnit4::class)
35 | class FullyDrawnStartupBenchmark {
36 | @get:Rule
37 | val benchmarkRule = MacrobenchmarkRule()
38 |
39 | @Test
40 | fun startup() = benchmarkRule.measureRepeated(
41 | packageName = TARGET_PACKAGE,
42 | metrics = listOf(StartupTimingMetric()),
43 | startupMode = StartupMode.COLD,
44 | iterations = DEFAULT_ITERATIONS,
45 | ) {
46 | uiAutomator {
47 | startIntent(Intent("$TARGET_PACKAGE.FULLY_DRAWN_STARTUP_ACTIVITY"))
48 |
49 | // Waits for an element that corresponds to fully drawn state
50 | onElement { textAsString() == "Fully Drawn" && isVisibleToUser }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/baselineprofile/SampleBaselineProfileGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.baselineprofile
18 |
19 | import androidx.benchmark.macro.junit4.BaselineProfileRule
20 | import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
21 | import androidx.test.uiautomator.textAsString
22 | import androidx.test.uiautomator.uiAutomator
23 | import com.example.macrobenchmark.benchmark.util.TARGET_PACKAGE
24 | import org.junit.Rule
25 | import org.junit.Test
26 | import org.junit.runner.RunWith
27 |
28 | @RunWith(AndroidJUnit4ClassRunner::class)
29 | class SampleBaselineProfileGenerator {
30 |
31 | @get:Rule
32 | val rule = BaselineProfileRule()
33 |
34 | @Test
35 | fun generate() {
36 | rule.collect(
37 | packageName = TARGET_PACKAGE,
38 | maxIterations = 15,
39 | stableIterations = 3
40 | ) {
41 | uiAutomator {
42 | startApp(TARGET_PACKAGE)
43 | listOf(
44 | "Login",
45 | "ListView",
46 | "Compose",
47 | "ScrollView",
48 | "Fully Drawn",
49 | "RecyclerView",
50 | "Nested RecyclerView",
51 | "Nested RecyclerView with Pools"
52 | ).forEach {
53 | onElement { textAsString() == it }.click()
54 | pressBack()
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/java/com/example/benchmark/ui/SortingAlgorithms.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.benchmark.ui
18 |
19 | /**
20 | * Some trivial sorting algorithms for benchmarks
21 | */
22 | object SortingAlgorithms {
23 |
24 | fun bubbleSort(array: IntArray) {
25 | for (i in 0..array.lastIndex) {
26 | for (j in 0 until array.lastIndex) {
27 | if (array[j] > array[j + 1]) {
28 | swap(array, j, j + 1)
29 | }
30 | }
31 | }
32 | }
33 |
34 | fun quickSort(array: IntArray, begin: Int = 0, end: Int = array.lastIndex) {
35 | if (end <= begin) return
36 | val pivot = partition(array, begin, end)
37 |
38 | quickSort(array, begin, pivot - 1)
39 | quickSort(array, pivot + 1, end)
40 | }
41 |
42 | private fun partition(array: IntArray, begin: Int, end: Int): Int {
43 | var counter = begin
44 | for (i in begin until end) {
45 | if (array[i] < array[end]) {
46 | swap(array, counter, i)
47 | counter++
48 | }
49 | }
50 | swap(array, end, counter)
51 | return counter
52 | }
53 |
54 | private fun swap(arr: IntArray, i: Int, j: Int) {
55 | val temp = arr[i]
56 | arr[i] = arr[j]
57 | arr[j] = temp
58 | }
59 | }
60 |
61 |
62 | /**
63 | * Check if the array is already sorted
64 | */
65 | val IntArray.isSorted
66 | get() = this
67 | .asSequence()
68 | .zipWithNext { a, b -> a <= b }
69 | .all { it }
70 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/recyclerview/Entry.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.recyclerview
18 |
19 | import android.view.LayoutInflater
20 | import android.view.View
21 | import android.view.ViewGroup
22 | import android.widget.TextView
23 | import androidx.appcompat.app.AlertDialog
24 | import androidx.recyclerview.widget.RecyclerView
25 | import com.example.macrobenchmark.target.R
26 | import com.example.macrobenchmark.target.util.ClickTrace
27 |
28 | data class Entry(val contents: String)
29 |
30 | class EntryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
31 | val content: TextView = itemView.findViewById(R.id.content)
32 | }
33 |
34 | class EntryAdapter(private val entries: List) : RecyclerView.Adapter() {
35 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EntryViewHolder {
36 | val inflater = LayoutInflater.from(parent.context)
37 | val itemView = inflater.inflate(R.layout.recycler_row, parent, false)
38 | itemView.findViewById(R.id.card).setOnClickListener {
39 | ClickTrace.onClickPerformed()
40 | AlertDialog.Builder(parent.context)
41 | .setMessage("Item clicked")
42 | .show()
43 | }
44 | return EntryViewHolder(itemView)
45 | }
46 |
47 | override fun onBindViewHolder(holder: EntryViewHolder, position: Int) {
48 | val entry = entries[position]
49 | holder.content.text = entry.contents
50 | }
51 |
52 | override fun getItemCount(): Int = entries.size
53 | }
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Android Performance
2 | ===================================
3 |
4 | A collection of samples using the performance libraries.
5 |
6 | You can learn more about Android Performance at https://d.android.com/performance.
7 |
8 | ## [Macrobenchmark sample](MacrobenchmarkSample)
9 |
10 | This sample shows how to use the Macrobenchmark library for testing application startup and runtime
11 | performance cases, such as scrolling a `RecyclerView` or `LazyColumn` to measure jank.
12 |
13 | **Baseline Profiles** can be generated with the Macrobenchmark library. This sample contains
14 | [code to generate a baseline profile](MacrobenchmarkSample/macrobenchmark/src/main/java/com/example/macrobenchmark/baselineprofile/).
15 |
16 | The sample also includes a [GitHub workflow](.github/workflows/firebase_test_lab.yml) to run
17 | Macrobenchmarks on Firebase Test Lab. For more information refer to the [README.md](MacrobenchmarkSample/ftl/README.md).
18 |
19 | The Macrobenchmark sample also demonstrates using [Jetpack Compose with Macrobenchmark](MacrobenchmarkSample/macrobenchmark/src/main/java/com/example/macrobenchmark/frames/FrameTimingBenchmark.kt#L72).
20 |
21 | ## [Microbenchmark sample](MicrobenchmarkSample)
22 |
23 | This sample shows how to use the Benchmark library to benchmark code and UI from library modules.
24 |
25 | ### Important Notes
26 |
27 | * Make sure your device's screen is on before running benchmarks
28 |
29 | ## [JankStats sample](JankStatsSample)
30 |
31 | This sample shows how to set up and use the JankStats library to detect janky frames.
32 |
33 | ## Reporting Issues
34 |
35 | You can report an [Issue with a sample](https://github.com/android/performance-samples/issues) using
36 | this repository. If you find an issue with a specific library, report it using the corresponding
37 | issue tracker link available in the sample README file.
38 |
39 | ## Additional Resources
40 |
41 | [PagingWithNetworkSample](https://github.com/googlesamples/android-architecture-components/tree/master/PagingWithNetworkSample)
42 | - Example of benchmarking an androidx.paging backed RecyclerView
43 |
44 | [WorkManagerSample](https://github.com/googlesamples/android-architecture-components/tree/master/WorkManagerSample)
45 | - Example of benchmarking asynchronously scheduled background work
46 |
47 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/navigation/nav_graph.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
28 |
31 |
32 |
33 |
38 |
41 |
42 |
43 |
47 |
50 |
51 |
55 |
56 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/build.gradle.kts:
--------------------------------------------------------------------------------
1 |
2 | plugins {
3 | id("com.android.library")
4 | id("kotlin-android")
5 | id("androidx.benchmark")
6 | }
7 |
8 | android {
9 | compileSdk = 35
10 |
11 | defaultConfig {
12 | minSdk = 21
13 |
14 | // Set this argument to capture profiling information, instead of measuring performance.
15 | // Can be one of:
16 | // * None
17 | // * StackSampling
18 | // * MethodTracing
19 | // See full descriptions of available options at: d.android.com/benchmark#profiling
20 | testInstrumentationRunnerArguments["androidx.benchmark.profiling.mode"] = "StackSampling"
21 | }
22 |
23 | testBuildType = "release"
24 |
25 | buildTypes {
26 | getByName("release") {
27 | // The androidx.benchmark plugin configures release buildType with proper settings, such as:
28 | // - disables code coverage
29 | // - adds CPU clock locking task
30 | // - signs release buildType with debug signing config
31 | // - copies benchmark results into build/outputs/connected_android_test_additional_output folder
32 | }
33 | }
34 |
35 | compileOptions {
36 | sourceCompatibility = JavaVersion.VERSION_18
37 | targetCompatibility = JavaVersion.VERSION_18
38 | }
39 |
40 | kotlinOptions {
41 | jvmTarget = JavaVersion.VERSION_18.toString()
42 | }
43 |
44 | namespace = "com.example.benchmark"
45 | }
46 |
47 | // [START benchmark_dependency]
48 | dependencies {
49 | androidTestImplementation(project(":benchmarkable"))
50 | androidTestImplementation(libs.benchmark)
51 | // [START_EXCLUDE]
52 |
53 | androidTestImplementation(libs.test.ext)
54 | androidTestImplementation(libs.test.rules)
55 | androidTestImplementation(libs.test.runner)
56 | androidTestImplementation(libs.junit)
57 | androidTestImplementation(libs.kotlin.stdlib)
58 |
59 | // duplicate the dependencies of ui module here, to ensure we have the same versions
60 | androidTestImplementation(libs.appcompat)
61 | androidTestImplementation(libs.cardview)
62 | androidTestImplementation(libs.constraintlayout)
63 | androidTestImplementation(libs.recyclerview)
64 | // [END_EXCLUDE]
65 | }
66 | // [END benchmark_dependency]
67 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/java/com/example/jankstats/MessageListAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.jankstats
18 |
19 | import android.view.LayoutInflater
20 | import android.view.ViewGroup
21 | import androidx.core.os.bundleOf
22 | import androidx.navigation.Navigation
23 | import androidx.recyclerview.widget.RecyclerView
24 | import com.example.jankstats.databinding.MessageItemBinding
25 |
26 | class MessageListAdapter(
27 | private val messageList: Array
28 | ) : RecyclerView.Adapter() {
29 |
30 | class MessageHeaderViewHolder(
31 | private val binding: MessageItemBinding
32 | ) : RecyclerView.ViewHolder(binding.root) {
33 |
34 | fun bind(headerText: String) {
35 | itemView.setOnClickListener {
36 | val bundle = bundleOf("title" to headerText)
37 | Navigation.findNavController(it).navigate(
38 | R.id.action_messageList_to_messageContent, bundle
39 | )
40 | }
41 | binding.messageHeader.text = headerText
42 | }
43 | }
44 |
45 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageHeaderViewHolder {
46 | val inflater = LayoutInflater.from(parent.context)
47 | val itemBinding = MessageItemBinding.inflate(inflater, parent, false)
48 | return MessageHeaderViewHolder(itemBinding)
49 | }
50 |
51 | override fun onBindViewHolder(holder: MessageHeaderViewHolder, position: Int) {
52 | holder.bind(messageList[position])
53 | }
54 |
55 | override fun getItemCount(): Int {
56 | return messageList.size
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/java/com/example/benchmark/TextBenchmarkUtils.kt:
--------------------------------------------------------------------------------
1 | package com.example.benchmark
2 |
3 | import kotlin.random.Random
4 |
5 | // a small number of strings reused frequently, expected to hit
6 | // in the word-granularity text layout cache
7 | private val CACHE_HIT_STRINGS =
8 | arrayOf("a", "small", "number", "of", "strings", "reused", "frequently")
9 |
10 | /**
11 | * This Random is reused by all benchmarks for selecting characters, so that each will select
12 | * different characters for each word, though the count and length of each word will be consistent.
13 | *
14 | * You can move this definition into the buildRandomParagraph function to see the affect of the
15 | * platform's text layout cache, when hitrate is artificially high due to string reuse.
16 | */
17 | private val unstableRandom = Random(0)
18 |
19 | private val chars: List = List(26) { 'a' + it } + List(26) { 'A' + it }
20 |
21 | /**
22 | * Builds a random paragraph string of words of ascii letters.
23 | *
24 | * @param hitPercentage defines the percentage of frequently used words. These words are expected to
25 | * have their layout information cached by the platform, and to be fast - 30% is a very rough
26 | * estimate of the hit rate in practice in English with arbitrary sample text.
27 | *
28 | * @param wordsInParagraph length of the paragraph to generate..
29 | */
30 | fun buildRandomParagraph(
31 | hitPercentage: Int = 30,
32 | wordsInParagraph: Int = 50
33 | ): String {
34 | require(!(hitPercentage < 0 || hitPercentage > 100))
35 | val stableRandom = Random(0)
36 |
37 | val result = StringBuilder()
38 | for (word in 0 until wordsInParagraph) {
39 | if (word != 0) {
40 | result.append(" ")
41 | }
42 | if (stableRandom.nextInt(100) < hitPercentage) {
43 | // add a common word, which is very likely to hit in the cache
44 | result.append(CACHE_HIT_STRINGS[stableRandom.nextInt(CACHE_HIT_STRINGS.size)])
45 | } else {
46 | // construct a random word, which will *most likely* miss in the layout cache
47 | for (j in 0 until stableRandom.nextInt(4, 9)) {
48 | // add random letter
49 | result.append(chars[unstableRandom.nextInt(chars.size)])
50 | }
51 | }
52 | }
53 | return result.toString()
54 | }
--------------------------------------------------------------------------------
/JankStatsSample/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | id("com.android.application")
19 | alias(libs.plugins.kotlin)
20 | alias(libs.plugins.compose.compiler)
21 | }
22 |
23 | android {
24 | compileSdk = 35
25 |
26 | buildFeatures {
27 | viewBinding = true
28 | compose = true
29 | }
30 |
31 | defaultConfig {
32 | applicationId = "com.example.jankstats"
33 | minSdk = 21
34 | targetSdk = 35
35 | versionCode = 1
36 | versionName = "1.0"
37 |
38 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
39 | }
40 |
41 | buildTypes {
42 | getByName("release") {
43 | isMinifyEnabled = false
44 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
45 | }
46 | }
47 | compileOptions {
48 | sourceCompatibility = JavaVersion.VERSION_11
49 | targetCompatibility = JavaVersion.VERSION_11
50 | }
51 | kotlinOptions {
52 | jvmTarget = "11"
53 | }
54 | composeOptions {
55 | kotlinCompilerExtensionVersion = "1.5.15"
56 | }
57 | namespace = "com.example.jankstats"
58 | }
59 |
60 | dependencies {
61 | val composeBom = platform(libs.compose.bom)
62 | implementation(composeBom)
63 | implementation(libs.activity)
64 | implementation(libs.appcompat)
65 | implementation(libs.compose.material)
66 | implementation(libs.compose.ui)
67 | implementation(libs.compose.ui.tooling)
68 | implementation(libs.constraintlayout)
69 | implementation(libs.jankstats)
70 | implementation(libs.material)
71 | implementation(libs.navigation.fragment)
72 | implementation(libs.navigation.ui)
73 | implementation(libs.tracing)
74 | }
75 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/java/com/example/benchmark/BitmapBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.benchmark
18 |
19 | import android.graphics.Bitmap
20 | import android.graphics.BitmapFactory
21 | import androidx.benchmark.junit4.BenchmarkRule
22 | import androidx.benchmark.junit4.measureRepeated
23 | import androidx.test.ext.junit.runners.AndroidJUnit4
24 | import androidx.test.filters.LargeTest
25 | import androidx.test.platform.app.InstrumentationRegistry
26 | import org.junit.Rule
27 | import org.junit.Test
28 | import org.junit.runner.RunWith
29 |
30 | private const val JETPACK = "images/jetpack.png"
31 |
32 | @LargeTest
33 | @RunWith(AndroidJUnit4::class)
34 | class BitmapBenchmark {
35 |
36 | @get:Rule
37 | val benchmarkRule = BenchmarkRule()
38 |
39 | private val context = InstrumentationRegistry.getInstrumentation().targetContext
40 |
41 | // Retrieve the bitmap from assets
42 | private val bitmap: Bitmap = context.assets.open(JETPACK).use { BitmapFactory.decodeStream(it) }
43 |
44 | /**
45 | * Measure the cost of many relatively cheaper JNI calls to fetch a row of pixels, one pixel at
46 | * a time.
47 | */
48 | @Test
49 | fun bitmapGetPixelBenchmark() {
50 | val pixels = IntArray(100) { it }
51 | benchmarkRule.measureRepeated {
52 | pixels.map { bitmap.getPixel(it, 0) }
53 | }
54 | }
55 |
56 | /**
57 | * Measure the cost of a single expensive JNI call to fetch a row of 100 pixels.
58 | */
59 | @Test
60 | fun bitmapGetPixelsBenchmark() {
61 | val pixels = IntArray(100) { it }
62 | benchmarkRule.measureRepeated {
63 | bitmap.getPixels(pixels, 0, 100, 0, 0, 100, 1)
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/java/com/example/benchmark/SortingBenchmarks.kt:
--------------------------------------------------------------------------------
1 | package com.example.benchmark
2 |
3 | import androidx.benchmark.junit4.BenchmarkRule
4 | import androidx.benchmark.junit4.measureRepeated
5 | import androidx.test.ext.junit.runners.AndroidJUnit4
6 | import com.example.benchmark.ui.SortingAlgorithms
7 | import com.example.benchmark.ui.isSorted
8 | import org.junit.Assert.assertTrue
9 | import org.junit.Rule
10 | import org.junit.Test
11 | import org.junit.runner.RunWith
12 | import kotlin.random.Random
13 |
14 |
15 | @RunWith(AndroidJUnit4::class)
16 | class SortingBenchmarks {
17 |
18 | @get:Rule
19 | val benchmarkRule = BenchmarkRule()
20 |
21 | // [START benchmark_with_timing_disabled]
22 | // using random with the same seed, so that it generates the same data every run
23 | private val random = Random(0)
24 |
25 | // create the array once and just copy it in benchmarks
26 | private val unsorted = IntArray(10_000) { random.nextInt() }
27 |
28 | @Test
29 | fun benchmark_quickSort() {
30 | // [START_EXCLUDE]
31 | // creating the variable outside of the measureRepeated to be able to assert after done
32 | var listToSort = intArrayOf()
33 | // [END_EXCLUDE]
34 | benchmarkRule.measureRepeated {
35 | // copy the array with timing disabled to measure only the algorithm itself
36 | listToSort = runWithTimingDisabled { unsorted.copyOf() }
37 |
38 | // sort the array in place and measure how long it takes
39 | SortingAlgorithms.quickSort(listToSort)
40 | }
41 |
42 | // assert only once not to add overhead to the benchmarks
43 | assertTrue(listToSort.isSorted)
44 | }
45 | // [END benchmark_with_timing_disabled]
46 |
47 | @Test
48 | fun benchmark_bubbleSort() {
49 | // creating the variable outside of the measureRepeated to be able to assert after done
50 | var listToSort = intArrayOf()
51 |
52 | benchmarkRule.measureRepeated {
53 | // copy the array with timing disabled to measure only the algorithm itself
54 | listToSort = runWithTimingDisabled { unsorted.copyOf() }
55 |
56 | // sort the array in place and measure how long it takes
57 | SortingAlgorithms.bubbleSort(listToSort)
58 | }
59 |
60 | // assert only once not to add overhead to the benchmarks
61 | assertTrue(listToSort.isSorted)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
23 |
24 |
25 |
31 |
34 |
37 |
38 |
39 |
40 |
46 |
--------------------------------------------------------------------------------
/JankStatsSample/README.md:
--------------------------------------------------------------------------------
1 | JankStats Sample
2 | ===================================
3 |
4 | This sample project shows how to use the JankStats library.
5 |
6 | It includes multiple simple sample activities:
7 |
8 | * [JankLoggingActivity](app/src/main/java/com/example/jankstats/JankLoggingActivity.kt)
9 | creates a JankStats object and tracks the performance of a simple RecyclerView
10 | UI, logging every frame to logcat via the JankStats API.
11 |
12 | * [JankAggregatorActivity](app/src/main/java/com/example/jankstats/JankAggregatorActivity.kt)
13 | creates an activity much like JankLoggingActivity, but with an aggregation layer
14 | on top, using the utility class
15 | [JankStatsAggregator](app/src/main/java/com/example/jankstats/JankStatsAggregator.kt).
16 | JankStatsAggregator uses the same per-frame mechanism of JankStats under the hood,
17 | but collects that per-frame information and saves it for later logging when the client
18 | asks for it.
19 |
20 | ### Running
21 |
22 | Open the project in Android Studio and run the activities as you usually would.
23 | Scrolling the RecyclerView will cause performance metrics to be sent to the activity.
24 | In the case of JankLoggingActivity, that data will be output inline to logcat.
25 | For JankAggregatorActivity, the data will be cached internally and output later,
26 | when requested (which for that activity happens when it goes to the background).
27 |
28 | ### Reporting Issues
29 |
30 | You can report an [Issue with the sample](https://github.com/android/performance-samples/issues) using this repository. If you find an issue with the JankStats library, report it using the [Issue Tracker](https://goo.gle/metrics-issue).
31 |
32 | License
33 | -------
34 |
35 | Copyright 2022 The Android Open Source Project, Inc.
36 |
37 | Licensed to the Apache Software Foundation (ASF) under one or more contributor
38 | license agreements. See the NOTICE file distributed with this work for
39 | additional information regarding copyright ownership. The ASF licenses this
40 | file to you under the Apache License, Version 2.0 (the "License"); you may not
41 | use this file except in compliance with the License. You may obtain a copy of
42 | the License at
43 |
44 | http://www.apache.org/licenses/LICENSE-2.0
45 |
46 | Unless required by applicable law or agreed to in writing, software
47 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
48 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
49 | License for the specific language governing permissions and limitations under
50 | the License.
51 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/benchmarkable/src/main/res/layout/item_card.xml:
--------------------------------------------------------------------------------
1 |
16 |
22 |
23 |
27 |
28 |
37 |
38 |
48 |
49 |
59 |
60 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/benchmark/frames/TextInputFrameTimingBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.benchmark.frames
18 |
19 | import android.content.Intent
20 | import androidx.benchmark.macro.CompilationMode
21 | import androidx.benchmark.macro.FrameTimingMetric
22 | import androidx.benchmark.macro.StartupMode
23 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
24 | import androidx.test.ext.junit.runners.AndroidJUnit4
25 | import androidx.test.filters.LargeTest
26 | import androidx.test.uiautomator.uiAutomator
27 | import com.example.macrobenchmark.benchmark.util.DEFAULT_ITERATIONS
28 | import com.example.macrobenchmark.benchmark.util.TARGET_PACKAGE
29 | import org.junit.Rule
30 | import org.junit.Test
31 | import org.junit.runner.RunWith
32 |
33 | @LargeTest
34 | @RunWith(AndroidJUnit4::class)
35 | class TextInputFrameTimingBenchmark {
36 | @get:Rule
37 | val benchmarkRule = MacrobenchmarkRule()
38 |
39 | @Test
40 | fun inputText() {
41 | benchmarkRule.measureRepeated(
42 | packageName = TARGET_PACKAGE,
43 | metrics = listOf(FrameTimingMetric()),
44 | // Try switching to different compilation modes to see the effect
45 | // it has on frame timing metrics.
46 | compilationMode = CompilationMode.None(),
47 | startupMode = StartupMode.WARM, // restarts activity each iteration
48 | iterations = DEFAULT_ITERATIONS,
49 | setupBlock = {
50 | uiAutomator {
51 | // Before starting to measure, navigate to the UI to be measured
52 | startIntent(Intent("$packageName.COMPOSE_ACTIVITY"))
53 | }
54 | }
55 | ) {
56 | uiAutomator {
57 | onElement { isEditable }.text = "Benchmark input"
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/activity/FullyDrawnStartupActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.activity
18 |
19 | import android.os.Bundle
20 | import androidx.activity.ComponentActivity
21 | import androidx.activity.compose.ReportDrawnWhen
22 | import androidx.activity.compose.setContent
23 | import androidx.activity.viewModels
24 | import androidx.compose.foundation.layout.Box
25 | import androidx.compose.foundation.layout.Column
26 | import androidx.compose.material3.CircularProgressIndicator
27 | import androidx.compose.material3.MaterialTheme
28 | import androidx.compose.material3.Text
29 | import androidx.compose.runtime.LaunchedEffect
30 | import androidx.compose.runtime.getValue
31 | import androidx.compose.runtime.mutableStateOf
32 | import androidx.compose.runtime.remember
33 | import androidx.compose.runtime.setValue
34 | import com.example.macrobenchmark.target.util.SampleViewModel
35 |
36 | class FullyDrawnStartupActivity : ComponentActivity() {
37 |
38 | private val sampleViewModel: SampleViewModel by viewModels()
39 |
40 | override fun onCreate(savedInstanceState: Bundle?) {
41 | super.onCreate(savedInstanceState)
42 | setContent {
43 | MaterialTheme {
44 | Box {
45 | var isLoaded by remember { mutableStateOf(false) }
46 | ReportDrawnWhen { isLoaded }
47 |
48 | LaunchedEffect(Unit) {
49 | isLoaded = sampleViewModel.data.isReady()
50 | }
51 |
52 | Column {
53 | Text("Compose Macrobenchmark Target")
54 | if (!isLoaded) {
55 | CircularProgressIndicator()
56 | } else {
57 | Text("Fully Drawn")
58 | }
59 | }
60 | }
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/.github/workflows/macrobenchmark.yml:
--------------------------------------------------------------------------------
1 | name: Builds Macrobenchmarks
2 |
3 | on:
4 | workflow_dispatch:
5 | pull_request:
6 | push:
7 | branches: [ macrobenchmark, main ]
8 |
9 | jobs:
10 | # This job checks for any file changed within MacrobenchmarkSample/ folder
11 | # to distinguish if the build check for Macrobenchmark is needed to be run.
12 | # It sets the outputs.macrobenchmark to true/false based on the changes.
13 | # In the next build job, it checks for needs.changes.outputs.macrobenchmark == 'true'
14 | # or skips the job otherwise.
15 | changes:
16 | if: github.repository_owner == 'android'
17 | runs-on: ubuntu-latest
18 | # Set job outputs to values from filter step to be able to use it in next job
19 | outputs:
20 | macrobenchmark: ${{ steps.filter.outputs.macrobenchmark }}
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | - uses: dorny/paths-filter@v3
25 | id: filter
26 | with:
27 | filters: |
28 | macrobenchmark:
29 | - 'MacrobenchmarkSample/**'
30 |
31 | build:
32 | needs: changes
33 | # Only run action for the main repo & not forks and if change is in macrobenchmark sample
34 | if: github.repository_owner == 'android' && (needs.changes.outputs.macrobenchmark == 'true' || github.event_name == 'workflow_dispatch')
35 | runs-on: macos-latest
36 | defaults:
37 | run:
38 | working-directory: ./MacrobenchmarkSample
39 | steps:
40 | - name: Checkout
41 | uses: actions/checkout@v4
42 |
43 | - name: Setup JDK
44 | id: setup-java
45 | uses: actions/setup-java@v4
46 | with:
47 | java-version: '17'
48 | distribution: 'zulu'
49 |
50 | - name: Setup Gradle
51 | uses: gradle/actions/setup-gradle@v4
52 | with:
53 | gradle-home-cache-cleanup: true
54 |
55 | - name: Build Macrobenchmark Sample
56 | run: >
57 | ./gradlew assemble \
58 | -x :macrobenchmark:pixel6Api31Setup \
59 | -x :macrobenchmark:pixel6Api31NonMinifiedReleaseAndroidTest \
60 | -x :macrobenchmark:collectNonMinifiedReleaseBaselineProfile \
61 | -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules="none" \
62 | -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" \
63 | -Pandroid.experimental.testOptions.managedDevices.setupTimeoutMinutes=180 \
64 | -Pandroid.experimental.androidTest.numManagedDeviceShards=1 \
65 | -Pandroid.experimental.testOptions.managedDevices.maxConcurrentDevices=1 \
66 | -Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true
67 |
68 |
--------------------------------------------------------------------------------
/JankStatsSample/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | # Copyright 2022 The Android Open Source Project
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | [versions]
16 |
17 | agp = "8.11.1"
18 | appcompat = "1.7.1"
19 | activity = "1.10.1"
20 | composeBom = "2025.07.00"
21 | coroutines = "1.10.2"
22 | constraint_layout = "2.2.1"
23 | core = "1.9.0"
24 | jankstats = "1.0.0-beta02"
25 | kotlin = "2.2.0"
26 | material = "1.12.0"
27 | navigation = "2.9.3"
28 | tracing = "1.3.0"
29 |
30 | [libraries]
31 |
32 | compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
33 | activity = { group = "androidx.activity", name = "activity-ktx", version.ref = "activity" }
34 | appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
35 | compose-activity = { group = "androidx.activity", name = "activity-compose" }
36 | compose-material = { group = "androidx.compose.material", name = "material" }
37 | compose-ui = { group = "androidx.compose.ui", name = "ui" }
38 | compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling"}
39 | constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraint_layout" }
40 | jankstats = { group = "androidx.metrics", name = "metrics-performance", version.ref = "jankstats" }
41 | kotlin-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
42 | kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
43 | material = { group = "com.google.android.material", name = "material", version.ref = "material"}
44 | navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation"}
45 | navigation-ui = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation"}
46 | tracing = { group = "androidx.tracing", name = "tracing-ktx", version.ref = "tracing"}
47 |
48 | [plugins]
49 |
50 | application = { id = "com.android.application", version.ref = "agp" }
51 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
52 | kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
53 | test = { id = "com.android.test", version.ref = "agp" }
54 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/res/layout/item_parent.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
24 |
25 |
35 |
36 |
47 |
48 |
59 |
--------------------------------------------------------------------------------
/JankStatsSample/app/src/main/res/layout/activity_jank_logging.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
23 |
24 |
31 |
32 |
37 |
38 |
39 |
40 |
51 |
52 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/functions/samples/sample_benchmark_result_1.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "build": {
4 | "brand": "google",
5 | "device": "redfin",
6 | "fingerprint": "google/redfin/redfin:S/SP1A.210202.002/7117294:userdebug/dev-keys",
7 | "model": "Pixel 5",
8 | "version": {
9 | "sdk": 30
10 | }
11 | },
12 | "cpuCoreCount": 8,
13 | "cpuLocked": false,
14 | "cpuMaxFreqHz": 2400000000,
15 | "memTotalBytes": 7818850304,
16 | "sustainedPerformanceModeEnabled": false
17 | },
18 | "benchmarks": [
19 | {
20 | "name": "start",
21 | "params": {},
22 | "className": "com.example.macrobenchmark.FrameTimingBenchmark",
23 | "totalRunTimeNs": 320055806102,
24 | "metrics": {
25 | "frameTime50thPercentileMs": {
26 | "minimum": 5,
27 | "maximum": 5,
28 | "median": 5,
29 | "runs": [
30 | 5,
31 | 5,
32 | 5,
33 | 5,
34 | 5,
35 | 5,
36 | 5,
37 | 5,
38 | 5,
39 | 5
40 | ]
41 | },
42 | "frameTime90thPercentileMs": {
43 | "minimum": 5,
44 | "maximum": 5,
45 | "median": 5,
46 | "runs": [
47 | 5,
48 | 5,
49 | 5,
50 | 5,
51 | 5,
52 | 5,
53 | 5,
54 | 5,
55 | 5,
56 | 5
57 | ]
58 | },
59 | "frameTime95thPercentileMs": {
60 | "minimum": 5,
61 | "maximum": 6,
62 | "median": 5,
63 | "runs": [
64 | 6,
65 | 6,
66 | 6,
67 | 5,
68 | 5,
69 | 5,
70 | 5,
71 | 5,
72 | 5,
73 | 5
74 | ]
75 | },
76 | "frameTime99thPercentileMs": {
77 | "minimum": 7,
78 | "maximum": 9,
79 | "median": 8,
80 | "runs": [
81 | 9,
82 | 9,
83 | 7,
84 | 7,
85 | 7,
86 | 8,
87 | 9,
88 | 8,
89 | 7,
90 | 7
91 | ]
92 | },
93 | "totalFrameCount": {
94 | "minimum": 566,
95 | "maximum": 730,
96 | "median": 596,
97 | "runs": [
98 | 597,
99 | 567,
100 | 596,
101 | 597,
102 | 595,
103 | 593,
104 | 566,
105 | 599,
106 | 567,
107 | 730
108 | ]
109 | }
110 | },
111 | "warmupIterations": 0,
112 | "repeatIterations": 10,
113 | "thermalThrottleSleepSeconds": 0
114 | }
115 | ]
116 | }
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/activity/clicklatency/RecyclerViewActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.activity.clicklatency
18 |
19 | import android.os.Bundle
20 | import androidx.appcompat.app.AppCompatActivity
21 | import androidx.core.view.ViewCompat
22 | import androidx.core.view.WindowInsetsCompat
23 | import androidx.core.view.updatePadding
24 | import androidx.recyclerview.widget.LinearLayoutManager
25 | import com.example.macrobenchmark.target.databinding.ActivityRecyclerViewBinding
26 | import com.example.macrobenchmark.target.recyclerview.Entry
27 | import com.example.macrobenchmark.target.recyclerview.EntryAdapter
28 |
29 | open class RecyclerViewActivity : AppCompatActivity() {
30 | override fun onCreate(savedInstanceState: Bundle?) {
31 | super.onCreate(savedInstanceState)
32 | title = "RecyclerView Sample"
33 | val binding = ActivityRecyclerViewBinding.inflate(layoutInflater)
34 | setContentView(binding.root)
35 |
36 | ViewCompat.setOnApplyWindowInsetsListener(binding.recycler) { v, insets ->
37 | val bars = insets.getInsets(
38 | WindowInsetsCompat.Type.systemBars()
39 | or WindowInsetsCompat.Type.displayCutout()
40 | )
41 | v.updatePadding(
42 | left = bars.left,
43 | top = bars.top,
44 | right = bars.right,
45 | bottom = bars.bottom,
46 | )
47 | WindowInsetsCompat.CONSUMED
48 | }
49 |
50 | // This argument allows the Macrobenchmark tests control the content being tested.
51 | // In your app, you could use this approach to navigate to a consistent UI.
52 | // e.g. Here the UI is being populated with a well known number of list items.
53 | val itemCount = intent.getIntExtra(EXTRA_ITEM_COUNT, 1000)
54 | val adapter = EntryAdapter(entries(itemCount))
55 | binding.recycler.layoutManager = LinearLayoutManager(this)
56 | binding.recycler.adapter = adapter
57 | }
58 |
59 | private fun entries(size: Int) = List(size) {
60 | Entry("Item $it")
61 | }
62 |
63 | companion object {
64 | const val EXTRA_ITEM_COUNT = "ITEM_COUNT"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/benchmark/startup/SmallListStartupBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.benchmark.startup
18 |
19 | import android.content.Intent
20 | import androidx.benchmark.macro.StartupMode
21 | import androidx.benchmark.macro.StartupTimingMetric
22 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
23 | import androidx.test.filters.LargeTest
24 | import androidx.test.uiautomator.uiAutomator
25 | import com.example.macrobenchmark.benchmark.util.DEFAULT_ITERATIONS
26 | import com.example.macrobenchmark.benchmark.util.TARGET_PACKAGE
27 | import org.junit.Rule
28 | import org.junit.Test
29 | import org.junit.runner.RunWith
30 | import org.junit.runners.Parameterized
31 |
32 | private const val LIST_ITEMS = 5
33 |
34 | @LargeTest
35 | @RunWith(Parameterized::class)
36 | class SmallListStartupBenchmark(
37 | private val startupMode: StartupMode
38 | ) {
39 | @get:Rule
40 | val benchmarkRule = MacrobenchmarkRule()
41 |
42 | @Test
43 | fun startup() = benchmarkRule.measureRepeated(
44 | packageName = TARGET_PACKAGE,
45 | metrics = listOf(StartupTimingMetric()),
46 | iterations = DEFAULT_ITERATIONS,
47 | startupMode = startupMode,
48 | ) {
49 | uiAutomator {
50 | startIntent(
51 | Intent("$packageName.RECYCLER_VIEW_ACTIVITY").putExtra(
52 | "ITEM_COUNT",
53 | LIST_ITEMS
54 | )
55 | )
56 | }
57 | }
58 |
59 | @Test
60 | fun startupCompose() = benchmarkRule.measureRepeated(
61 | packageName = TARGET_PACKAGE,
62 | metrics = listOf(StartupTimingMetric()),
63 | iterations = DEFAULT_ITERATIONS,
64 | startupMode = startupMode,
65 | ) {
66 | uiAutomator {
67 | startIntent(
68 | Intent("$packageName.COMPOSE_ACTIVITY")
69 | .putExtra("ITEM_COUNT", LIST_ITEMS)
70 | )
71 | }
72 | }
73 |
74 | companion object {
75 | @Parameterized.Parameters(name = "mode={0}")
76 | @JvmStatic
77 | fun parameters(): List> {
78 | return listOf(
79 | StartupMode.COLD,
80 | StartupMode.WARM
81 | ).map { arrayOf(it) }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/MicrobenchmarkSample/microbenchmark/src/androidTest/java/com/example/benchmark/ViewMeasureLayoutBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.benchmark
18 |
19 | import android.view.LayoutInflater
20 | import android.view.View.MeasureSpec
21 | import androidx.benchmark.junit4.BenchmarkRule
22 | import androidx.benchmark.junit4.measureRepeated
23 | import androidx.test.ext.junit.runners.AndroidJUnit4
24 | import androidx.test.filters.LargeTest
25 | import androidx.test.platform.app.InstrumentationRegistry
26 | import com.example.benchmark.ui.R
27 | import org.junit.Rule
28 | import org.junit.Test
29 | import org.junit.runner.OrderWith
30 | import org.junit.runner.RunWith
31 | import org.junit.runner.manipulation.Alphanumeric
32 |
33 | @LargeTest
34 | @RunWith(AndroidJUnit4::class)
35 | @OrderWith(Alphanumeric::class)
36 | class ViewMeasureLayoutBenchmark {
37 |
38 | @get:Rule
39 | val benchmarkRule = BenchmarkRule()
40 |
41 | @Test
42 | fun constraintLayoutHierarchy_AT_MOST() {
43 | benchmarkMeasureLayout(R.layout.activity_constraintlayout, MeasureSpec.AT_MOST)
44 | }
45 |
46 | @Test
47 | fun constraintLayoutHierarchy_EXACTLY() {
48 | benchmarkMeasureLayout(R.layout.activity_constraintlayout, MeasureSpec.EXACTLY)
49 | }
50 |
51 | @Test
52 | fun traditionalViewHierarchy_AT_MOST() {
53 | benchmarkMeasureLayout(R.layout.activity_traditional, MeasureSpec.AT_MOST)
54 | }
55 |
56 | @Test
57 | fun traditionalViewHierarchy_EXACTLY() {
58 | benchmarkMeasureLayout(R.layout.activity_traditional, MeasureSpec.EXACTLY)
59 | }
60 |
61 | private fun benchmarkMeasureLayout(layoutRes: Int, mode: Int) {
62 | val context = InstrumentationRegistry.getInstrumentation().context
63 | val inflater = LayoutInflater.from(context)
64 |
65 | benchmarkRule.measureRepeated {
66 | // Not to use the view cache in the View class, we inflate it every time
67 | val container = runWithTimingDisabled { inflater.inflate(layoutRes, null) }
68 |
69 | val widthMeasureSpec = MeasureSpec.makeMeasureSpec(1080, mode)
70 | val heightMeasureSpec = MeasureSpec.makeMeasureSpec(1920, mode)
71 |
72 | container.measure(widthMeasureSpec, heightMeasureSpec)
73 | container.layout(0, 0, container.measuredWidth, container.measuredHeight)
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/util/SampleViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.util
18 |
19 | import android.app.Application
20 | import android.util.Log
21 | import androidx.datastore.preferences.core.edit
22 | import androidx.lifecycle.AndroidViewModel
23 | import androidx.lifecycle.viewModelScope
24 | import kotlinx.coroutines.flow.Flow
25 | import kotlinx.coroutines.flow.map
26 | import kotlinx.coroutines.launch
27 |
28 | /** ViewModel for this sample */
29 | class SampleViewModel(application: Application) : AndroidViewModel(application) {
30 |
31 | private val _data = SampleData()
32 | private val _login = AppLogin()
33 |
34 | val data: SampleData = _data
35 | val login = _login
36 |
37 | init {
38 | loadAppLogin()
39 | }
40 |
41 | /**
42 | * Attempts to log in with the provided credentials.
43 | * @return `true` if successful, `false` otherwise.
44 | */
45 | suspend fun login(userName: String, password: String) {
46 | _login.userName = userName
47 | _login.password = password
48 |
49 | val context = getApplication()
50 | context.dataStore.edit { settings ->
51 | settings[KEY_USER] = userName
52 | settings[KEY_PASSWORD] = password
53 | Log.d(TAG, "Wrote AppLogin to Data Store.")
54 | }
55 | }
56 |
57 | fun loginSynchronous(userName: String, password: String) {
58 | viewModelScope.launch {
59 | login(userName, password)
60 | }
61 | }
62 |
63 | /**
64 | * Loads existing [AppLogin] data asynchronously.
65 | */
66 | private fun loadAppLogin() {
67 | viewModelScope.launch {
68 | loadAppLoginFromPreferences().collect() {
69 | _login.userName = it.userName
70 | _login.password = it.password
71 | }
72 | }
73 | }
74 |
75 | /**
76 | * Loads [AppLogin] from local data store.
77 | */
78 | private fun loadAppLoginFromPreferences(): Flow =
79 | getApplication().dataStore.data.map { settings ->
80 | val userName = settings[KEY_USER] ?: ""
81 | val password = settings[KEY_PASSWORD] ?: ""
82 | Log.d(TAG, "Read AppLogin from Data Store.")
83 | AppLogin(userName = userName, password = password)
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/activity/clicklatency/NestedRecyclerActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.activity.clicklatency
18 |
19 | import android.os.Bundle
20 | import androidx.activity.viewModels
21 | import androidx.appcompat.app.AppCompatActivity
22 | import androidx.core.view.ViewCompat
23 | import androidx.core.view.WindowInsetsCompat
24 | import androidx.core.view.updatePadding
25 | import androidx.lifecycle.Lifecycle
26 | import androidx.lifecycle.lifecycleScope
27 | import androidx.lifecycle.repeatOnLifecycle
28 | import androidx.recyclerview.widget.LinearLayoutManager
29 | import com.example.macrobenchmark.target.databinding.ActivityNestedRvBinding
30 | import com.example.macrobenchmark.target.recyclerview.NestedRecyclerViewModel
31 | import com.example.macrobenchmark.target.recyclerview.ParentAdapter
32 | import kotlinx.coroutines.launch
33 |
34 | class NestedRecyclerActivity : AppCompatActivity() {
35 |
36 | private val viewModel by viewModels()
37 |
38 | private lateinit var binding: ActivityNestedRvBinding
39 |
40 | override fun onCreate(savedInstanceState: Bundle?) {
41 | super.onCreate(savedInstanceState)
42 |
43 | val useRecyclerPools = intent.getBooleanExtra(USE_RECYCLER_POOLS, false)
44 |
45 | binding = ActivityNestedRvBinding.inflate(layoutInflater)
46 |
47 | setContentView(binding.root)
48 |
49 | val adapter = ParentAdapter(useRecyclerPools)
50 |
51 | binding.recycler.adapter = adapter
52 | binding.recycler.layoutManager = LinearLayoutManager(this)
53 |
54 | lifecycleScope.launch {
55 | repeatOnLifecycle(Lifecycle.State.STARTED) {
56 | viewModel.items.collect {
57 | adapter.submitList(it)
58 | }
59 | }
60 | }
61 |
62 | ViewCompat.setOnApplyWindowInsetsListener(binding.recycler) { v, insets ->
63 | val bars = insets.getInsets(
64 | WindowInsetsCompat.Type.systemBars()
65 | or WindowInsetsCompat.Type.displayCutout()
66 | )
67 | v.updatePadding(
68 | left = bars.left,
69 | top = bars.top,
70 | right = bars.right,
71 | bottom = bars.bottom,
72 | )
73 | WindowInsetsCompat.CONSUMED
74 | }
75 | }
76 | }
77 |
78 | const val USE_RECYCLER_POOLS = "USE_RECYCLER_POOLS"
79 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/app/src/main/kotlin/com/example/macrobenchmark/target/recyclerview/NestedRecyclerViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.target.recyclerview
18 |
19 | import androidx.core.graphics.ColorUtils
20 | import androidx.lifecycle.ViewModel
21 | import kotlinx.coroutines.flow.MutableStateFlow
22 | import kotlinx.coroutines.flow.asStateFlow
23 | import kotlin.random.Random
24 |
25 | class NestedRecyclerViewModel : ViewModel() {
26 |
27 | private val _items = MutableStateFlow(emptyList())
28 | val items = _items.asStateFlow()
29 |
30 | // same random seed for each instance of ViewModel to generate the same items
31 | private val random = Random(0)
32 |
33 | init {
34 | _items.value = generateItems()
35 | }
36 |
37 | private fun generateItems() = List(PARENT_ITEM_COUNT) { parentId ->
38 | ParentItem(
39 | id = parentId.toString(),
40 | content = "Parent $parentId",
41 | children = generateChildren(parentId)
42 | )
43 | }
44 |
45 | private fun generateChildren(parentId: Int) = List(CHILD_ITEM_COUNT) { childId ->
46 | ChildItem(
47 | id = "${parentId}_${childId}",
48 | parentId = parentId.toString(),
49 | title = "Child $childId",
50 | subtitle = "Subtitle $parentId $childId",
51 | description = randomText(),
52 | color = generateColor(parentId, childId),
53 | inWishlist = random.nextBoolean(),
54 | likeState = LikeState.values().let { it[random.nextInt(it.size)] },
55 | )
56 | }
57 |
58 |
59 | private fun generateColor(parentId: Int, childId: Int) = ColorUtils.HSLToColor(
60 | floatArrayOf(
61 | parentId / PARENT_ITEM_COUNT.toFloat() * 360f,
62 | (CHILD_ITEM_COUNT - childId) / CHILD_ITEM_COUNT.toFloat(),
63 | 0.5f
64 | )
65 | )
66 |
67 |
68 | private fun randomText(words: Int = 30): String {
69 | val builder = StringBuilder(words)
70 |
71 | repeat(words) { w ->
72 | if (w != 0) {
73 | builder.append(" ")
74 | }
75 |
76 | // generate one word
77 | repeat(random.nextInt(4, 9)) { builder.append(chars[random.nextInt(chars.size)]) }
78 | }
79 |
80 | return builder.toString()
81 | }
82 | }
83 |
84 | private val chars = List(26) { 'A' + it } + List(26) { 'a' + it }
85 | private const val PARENT_ITEM_COUNT = 50
86 | private const val CHILD_ITEM_COUNT = 60
87 |
--------------------------------------------------------------------------------
/MacrobenchmarkSample/macrobenchmark/src/main/kotlin/com/example/macrobenchmark/benchmark/scroll/ScrollBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.macrobenchmark.benchmark.scroll
18 |
19 | import android.content.Intent
20 | import androidx.benchmark.macro.CompilationMode
21 | import androidx.benchmark.macro.ExperimentalMetricApi
22 | import androidx.benchmark.macro.FrameTimingMetric
23 | import androidx.benchmark.macro.StartupTimingMetric
24 | import androidx.benchmark.macro.TraceSectionMetric
25 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
26 | import androidx.test.ext.junit.runners.AndroidJUnit4
27 | import androidx.test.uiautomator.Direction
28 | import androidx.test.uiautomator.uiAutomator
29 | import com.example.macrobenchmark.benchmark.util.DEFAULT_ITERATIONS
30 | import com.example.macrobenchmark.benchmark.util.TARGET_PACKAGE
31 | import org.junit.Rule
32 | import org.junit.Test
33 | import org.junit.runner.RunWith
34 |
35 | @RunWith(AndroidJUnit4::class)
36 | @OptIn(ExperimentalMetricApi::class)
37 | class ScrollBenchmark {
38 |
39 | @get:Rule
40 | val benchmarkRule = MacrobenchmarkRule()
41 |
42 | @Test
43 | fun noCompilation() = scroll(CompilationMode.None())
44 |
45 | @Test
46 | fun defaultCompilation() = scroll(CompilationMode.DEFAULT)
47 |
48 | @Test
49 | fun full() = scroll(CompilationMode.Full())
50 |
51 | private fun scroll(compilationMode: CompilationMode) {
52 | var firstStart = true
53 | benchmarkRule.measureRepeated(
54 | packageName = TARGET_PACKAGE,
55 | metrics = listOf(
56 | TraceSectionMetric("ClickTrace"),
57 | StartupTimingMetric(),
58 | FrameTimingMetric()
59 | ),
60 | compilationMode = compilationMode,
61 | startupMode = null,
62 | iterations = DEFAULT_ITERATIONS,
63 | setupBlock = {
64 | if (firstStart) {
65 | uiAutomator {
66 | startIntent(Intent("$packageName.SCROLL_VIEW_ACTIVITY"))
67 | }
68 | firstStart = false
69 | }
70 | }
71 | ) {
72 | uiAutomator {
73 | onElement { isScrollable }.run {
74 | repeat(2) {
75 | fling(Direction.DOWN)
76 | }
77 | repeat(2) {
78 | fling(Direction.UP)
79 | }
80 | }
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------