├── 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 | 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 | --------------------------------------------------------------------------------