From 59284d0334e2ec3f3450809f67b65c48f3afbe88 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Mon, 17 Feb 2025 11:02:19 -0800 Subject: [PATCH 1/9] Setup a Macrobenchmark for RNTester Summary: Tests have been executed with Android Studio: **Startup** ``` # BEFORE timeToInitialDisplayMs min 216.9, median 222.9, max 245.3 Traces: Iteration 0 1 2 3 4 5 6 7 8 9 # AFTER timeToInitialDisplayMs min 213.8, median 220.4, max 237.9 Traces: Iteration 0 1 2 3 4 5 6 7 8 9 ``` **APK size** ``` -rw-r--r-- 1 ncor staff 20087367 Feb 17 17:37 before.apk -rw-r--r-- 1 ncor staff 20087399 Feb 17 18:43 after.apk ``` Changelog: [Internal] [Changed] - Differential Revision: D69753053 --- build.gradle.kts | 1 + .../react-native/gradle/libs.versions.toml | 9 + .../ReportFullyDrawnView.android.js | 13 ++ .../ReportFullyDrawnView.js | 13 ++ .../ReportFullyDrawnViewNativeComponent.js | 24 +++ .../android/app/benchmark/.gitignore | 1 + .../android/app/benchmark/build.gradle.kts | 52 ++++++ .../benchmark/src/main/AndroidManifest.xml | 1 + .../benchmark/ExampleStartupBenchmark.kt | 53 ++++++ .../rn-tester/android/app/build.gradle.kts | 8 +- .../android/app/src/main/AndroidManifest.xml | 169 +++++++++--------- .../facebook/react/uiapp/RNTesterActivity.kt | 3 + .../react/uiapp/RNTesterApplication.kt | 6 +- .../uiapp/component/ReportFullyDrawnView.kt | 28 +++ .../component/ReportFullyDrawnViewManager.kt | 36 ++++ packages/rn-tester/js/RNTesterAppShared.js | 69 +++---- settings.gradle.kts | 2 + 17 files changed, 372 insertions(+), 116 deletions(-) create mode 100644 packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnView.android.js create mode 100644 packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnView.js create mode 100644 packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js create mode 100644 packages/rn-tester/android/app/benchmark/.gitignore create mode 100644 packages/rn-tester/android/app/benchmark/build.gradle.kts create mode 100644 packages/rn-tester/android/app/benchmark/src/main/AndroidManifest.xml create mode 100644 packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt create mode 100644 packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/ReportFullyDrawnView.kt create mode 100644 packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/ReportFullyDrawnViewManager.kt diff --git a/build.gradle.kts b/build.gradle.kts index e71219373530..0227a891c3b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,6 +12,7 @@ plugins { alias(libs.plugins.download) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.binary.compatibility.validator) apply true + alias(libs.plugins.android.test) apply false } val reactAndroidProperties = java.util.Properties() diff --git a/packages/react-native/gradle/libs.versions.toml b/packages/react-native/gradle/libs.versions.toml index d0a15530a771..ba283e5ec061 100644 --- a/packages/react-native/gradle/libs.versions.toml +++ b/packages/react-native/gradle/libs.versions.toml @@ -42,6 +42,10 @@ fmt="11.0.2" folly="2024.11.18.00" glog="0.3.5" gtest="1.12.1" +junitVersion = "1.2.1" +espressoCore = "3.6.1" +uiautomator = "2.3.0" +benchmarkMacroJunit4 = "1.3.3" [libraries] androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } @@ -76,6 +80,10 @@ mockito = {module = "org.mockito:mockito-inline", version.ref = "mockito" } mockito-kotlin = {module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockito-kotlin" } robolectric = {module = "org.robolectric:robolectric", version.ref = "robolectric" } thoughtworks = {module = "com.thoughtworks.xstream:xstream", version.ref = "xstream" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } +androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmarkMacroJunit4" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } @@ -84,3 +92,4 @@ download = { id = "de.undercouch.download", version.ref = "download" } nexus-publish = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexus-publish" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } +android-test = { id = "com.android.test", version.ref = "agp" } diff --git a/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnView.android.js b/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnView.android.js new file mode 100644 index 000000000000..5fda7717f578 --- /dev/null +++ b/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnView.android.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import ReportFullyDrawnViewNativeComponent from './ReportFullyDrawnViewNativeComponent'; + +export default ReportFullyDrawnViewNativeComponent; diff --git a/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnView.js b/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnView.js new file mode 100644 index 000000000000..882c04ff6949 --- /dev/null +++ b/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnView.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import {View} from 'react-native'; + +export default View; diff --git a/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js b/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js new file mode 100644 index 000000000000..8050560d0eb7 --- /dev/null +++ b/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; +import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; + +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; + +type NativeProps = $ReadOnly<{ + ...ViewProps, +}>; + +export type ReportFullyDrawnViewType = HostComponent; + +export default (codegenNativeComponent( + 'RNTReportFullyDrawnView', +): ReportFullyDrawnViewType); diff --git a/packages/rn-tester/android/app/benchmark/.gitignore b/packages/rn-tester/android/app/benchmark/.gitignore new file mode 100644 index 000000000000..796b96d1c402 --- /dev/null +++ b/packages/rn-tester/android/app/benchmark/.gitignore @@ -0,0 +1 @@ +/build diff --git a/packages/rn-tester/android/app/benchmark/build.gradle.kts b/packages/rn-tester/android/app/benchmark/build.gradle.kts new file mode 100644 index 000000000000..d52ac7196534 --- /dev/null +++ b/packages/rn-tester/android/app/benchmark/build.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +plugins { + alias(libs.plugins.android.test) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.example.benchmark" + compileSdk = 35 + + defaultConfig { + minSdk = 24 + targetSdk = 35 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + // This benchmark buildType is used for benchmarking, and should function like your + // release build (for example, with minification on). It"s signed with a debug key + // for easy local/CI testing. + create("benchmark") { + isDebuggable = true + signingConfig = getByName("debug").signingConfig + matchingFallbacks += listOf("release") + } + } + + flavorDimensions += listOf("vm") + productFlavors { + create("hermes") { dimension = "vm" } + create("jsc") { dimension = "vm" } + } + + targetProjectPath = ":packages:rn-tester:android:app" + experimentalProperties["android.experimental.self-instrumenting"] = true +} + +dependencies { + implementation(libs.androidx.junit) + implementation(libs.androidx.espresso.core) + implementation(libs.androidx.uiautomator) + implementation(libs.androidx.benchmark.macro.junit4) +} + +androidComponents { beforeVariants(selector().all()) { it.enable = it.buildType == "benchmark" } } diff --git a/packages/rn-tester/android/app/benchmark/src/main/AndroidManifest.xml b/packages/rn-tester/android/app/benchmark/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..227314eeb7de --- /dev/null +++ b/packages/rn-tester/android/app/benchmark/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt b/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt new file mode 100644 index 000000000000..5b2c045ebd65 --- /dev/null +++ b/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.example.benchmark + +import androidx.benchmark.macro.StartupMode +import androidx.benchmark.macro.StartupTimingMetric +import androidx.benchmark.macro.junit4.MacrobenchmarkRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.uiautomator.By +import androidx.test.uiautomator.Until +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * This is an example startup benchmark. + * + * It navigates to the device's home screen, and launches the default activity. + * + * Before running this benchmark: + * 1) switch your app's active build variant in the Studio (affects Studio runs only) + * 2) add `` to your app's manifest, within the `` + * tag + * + * Run this benchmark from Studio to see startup measurements, and captured system traces for + * investigating your app's performance. + */ +@RunWith(AndroidJUnit4::class) +class ExampleStartupBenchmark { + @get:Rule val benchmarkRule = MacrobenchmarkRule() + + @Test + fun startup() = + benchmarkRule.measureRepeated( + packageName = "com.facebook.react.uiapp", + metrics = listOf(StartupTimingMetric()), + iterations = 10, + startupMode = StartupMode.COLD, + setupBlock = { + pressHome() + }) { + startActivityAndWait() + + // Waits for an element that corresponds to fully drawn state + device.wait(Until.hasObject(By.text("Components")), 10_000) + device.waitForIdle() + } +} diff --git a/packages/rn-tester/android/app/build.gradle.kts b/packages/rn-tester/android/app/build.gradle.kts index d7d263c303f8..a7868ac855c4 100644 --- a/packages/rn-tester/android/app/build.gradle.kts +++ b/packages/rn-tester/android/app/build.gradle.kts @@ -62,7 +62,7 @@ react { /* Hermes Commands */ // The hermes compiler command to run. By default it is 'hermesc' hermesCommand = "$reactNativeDirPath/ReactAndroid/hermes-engine/build/hermes/bin/hermesc" - enableHermesOnlyInVariants = listOf("hermesDebug", "hermesRelease") + enableHermesOnlyInVariants = listOf("hermesDebug", "hermesRelease", "hermesBenchmark") autolinkLibrariesWithApp() } @@ -142,6 +142,11 @@ android { proguardFiles(getDefaultProguardFile("proguard-android.txt")) signingConfig = signingConfigs.getByName("debug") } + create("benchmark") { + initWith(buildTypes.getByName("release")) + matchingFallbacks += listOf("release") + isDebuggable = false + } } sourceSets.named("main") { // SampleTurboModule. @@ -165,6 +170,7 @@ dependencies { "jscImplementation"(jscFlavor) testImplementation(libs.junit) + implementation("androidx.profileinstaller:profileinstaller:1.4.1") } android { diff --git a/packages/rn-tester/android/app/src/main/AndroidManifest.xml b/packages/rn-tester/android/app/src/main/AndroidManifest.xml index 720b58f79675..0783a82713fb 100644 --- a/packages/rn-tester/android/app/src/main/AndroidManifest.xml +++ b/packages/rn-tester/android/app/src/main/AndroidManifest.xml @@ -1,88 +1,97 @@ - + - - - - + + + + - - - - - - + + + + + + + + + - - - - + + + + - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt index e1394a77e0fb..13ed22d2e6cd 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt @@ -45,6 +45,9 @@ internal class RNTesterActivity : ReactActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + fullyDrawnReporter.addReporter() + // set background color so it will show below transparent system bars on forced edge-to-edge this.window?.setBackgroundDrawable(ColorDrawable(Color.BLACK)) // register insets listener to update margins on the ReactRootView to avoid overlap w/ system diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt index 2fe4530c3e27..738c20ca228a 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt @@ -30,6 +30,7 @@ import com.facebook.react.shell.MainReactPackage import com.facebook.react.soloader.OpenSourceMergedSoMapping import com.facebook.react.uiapp.component.MyLegacyViewManager import com.facebook.react.uiapp.component.MyNativeViewManager +import com.facebook.react.uiapp.component.ReportFullyDrawnViewManager import com.facebook.react.uimanager.ReactShadowNode import com.facebook.react.uimanager.ViewManager import com.facebook.soloader.SoLoader @@ -99,12 +100,12 @@ internal class RNTesterApplication : Application(), ReactApplication { ): List = emptyList() override fun getViewManagerNames(reactContext: ReactApplicationContext) = - listOf("RNTMyNativeView", "RNTMyLegacyNativeView") + listOf("RNTMyNativeView", "RNTMyLegacyNativeView", "RNTReportFullyDrawnView") override fun createViewManagers( reactContext: ReactApplicationContext ): List> = - listOf(MyNativeViewManager(), MyLegacyViewManager(reactContext)) + listOf(MyNativeViewManager(), MyLegacyViewManager(reactContext), ReportFullyDrawnViewManager()) override fun createViewManager( reactContext: ReactApplicationContext, @@ -113,6 +114,7 @@ internal class RNTesterApplication : Application(), ReactApplication { when (viewManagerName) { "RNTMyNativeView" -> MyNativeViewManager() "RNTMyLegacyNativeView" -> MyLegacyViewManager(reactContext) + "RNTReportFullyDrawnView" -> ReportFullyDrawnViewManager() else -> null } }) diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/ReportFullyDrawnView.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/ReportFullyDrawnView.kt new file mode 100644 index 000000000000..bd092b41b7e4 --- /dev/null +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/ReportFullyDrawnView.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uiapp.component + +import androidx.appcompat.app.AppCompatActivity +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.views.view.ReactViewGroup + +internal class ReportFullyDrawnView(context: ThemedReactContext) : ReactViewGroup(context) { + private val reactApplicationContext = context.reactApplicationContext + private var didReportFullyDrawn = false + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + super.onLayout(changed, left, top, right, bottom) + + if (!didReportFullyDrawn) { + didReportFullyDrawn = true + + val activity = reactApplicationContext.currentActivity as? AppCompatActivity + activity?.fullyDrawnReporter?.removeReporter() + } + } +} diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/ReportFullyDrawnViewManager.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/ReportFullyDrawnViewManager.kt new file mode 100644 index 000000000000..113af05c4d01 --- /dev/null +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/ReportFullyDrawnViewManager.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uiapp.component + +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.SimpleViewManager +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate +import com.facebook.react.viewmanagers.RNTReportFullyDrawnViewManagerDelegate +import com.facebook.react.viewmanagers.RNTReportFullyDrawnViewManagerInterface + +/** View manager for ReportFullyDrawnView components. */ +@ReactModule(name = ReportFullyDrawnViewManager.REACT_CLASS) +internal class ReportFullyDrawnViewManager : + ViewGroupManager(), + RNTReportFullyDrawnViewManagerInterface { + + companion object { + const val REACT_CLASS = "RNTReportFullyDrawnView" + } + + private val delegate: ViewManagerDelegate = RNTReportFullyDrawnViewManagerDelegate(this) + + override fun getDelegate(): ViewManagerDelegate = delegate + + override fun getName(): String = REACT_CLASS + + override fun createViewInstance(reactContext: ThemedReactContext): ReportFullyDrawnView = + ReportFullyDrawnView(reactContext) +} diff --git a/packages/rn-tester/js/RNTesterAppShared.js b/packages/rn-tester/js/RNTesterAppShared.js index e41e6cd640d3..91e10fd56de4 100644 --- a/packages/rn-tester/js/RNTesterAppShared.js +++ b/packages/rn-tester/js/RNTesterAppShared.js @@ -26,6 +26,7 @@ import { getExamplesListWithRecentlyUsed, initialNavigationState, } from './utils/testerStateUtils'; +import ReportFullyDrawnView from '../ReportFullyDrawnView/ReportFullyDrawnView'; import * as React from 'react'; import { BackHandler, @@ -265,40 +266,42 @@ const RNTesterApp = ({ screen === Screens.COMPONENTS ? examplesList.components : examplesList.apis; return ( - - - {activeModule && BackButtonComponent ? ( - - ) : undefined} - - - {activeModule != null ? ( - + + + {activeModule && BackButtonComponent ? ( + + ) : undefined} + + + {activeModule != null ? ( + + ) : ( + + )} + + + - ) : ( - - )} - - - - - + + + ); }; diff --git a/settings.gradle.kts b/settings.gradle.kts index 4a6e16a5aeaa..8d22d7f38c63 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -39,3 +39,5 @@ configure { autolinkLibrariesFromCommand( workingDirectory = file("packages/rn-tester/"), lockFiles = files("yarn.lock")) } + +include(":packages:rn-tester:android:app:benchmark") From 116f18b76d9b6b58c21a287b4657c63ec1d5d0d5 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Thu, 20 Feb 2025 15:02:28 -0500 Subject: [PATCH 2/9] fix deps for benchmark build --- packages/rn-tester/android/app/build.gradle.kts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/rn-tester/android/app/build.gradle.kts b/packages/rn-tester/android/app/build.gradle.kts index a7868ac855c4..f9182ae47728 100644 --- a/packages/rn-tester/android/app/build.gradle.kts +++ b/packages/rn-tester/android/app/build.gradle.kts @@ -207,6 +207,9 @@ afterEvaluate { tasks .getByName("createBundleHermesReleaseJsAndAssets") .dependsOn(":packages:react-native:ReactAndroid:hermes-engine:buildHermesC") + tasks + .getByName("createBundleHermesBenchmarkJsAndAssets") + .dependsOn(":packages:react-native:ReactAndroid:hermes-engine:buildHermesC") } // As we're building 4 native flavors in parallel, there is clash on the `.cxx/Debug` and @@ -230,4 +233,7 @@ afterEvaluate { tasks .getByName("createBundleHermesReleaseJsAndAssets") .dependsOn(":packages:react-native:ReactAndroid:buildCodegenCLI") + tasks + .getByName("createBundleHermesBenchmarkJsAndAssets") + .dependsOn(":packages:react-native:ReactAndroid:buildCodegenCLI") } From fb122e364d1e9c621de15f778dac7494bdd225fd Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Thu, 20 Feb 2025 15:20:31 -0500 Subject: [PATCH 3/9] fix deps part 2 --- packages/rn-tester/android/app/build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/rn-tester/android/app/build.gradle.kts b/packages/rn-tester/android/app/build.gradle.kts index f9182ae47728..a023c06356d6 100644 --- a/packages/rn-tester/android/app/build.gradle.kts +++ b/packages/rn-tester/android/app/build.gradle.kts @@ -233,6 +233,9 @@ afterEvaluate { tasks .getByName("createBundleHermesReleaseJsAndAssets") .dependsOn(":packages:react-native:ReactAndroid:buildCodegenCLI") + tasks + .getByName("createBundleJscBenchmarkJsAndAssets") + .dependsOn(":packages:react-native:ReactAndroid:buildCodegenCLI") tasks .getByName("createBundleHermesBenchmarkJsAndAssets") .dependsOn(":packages:react-native:ReactAndroid:buildCodegenCLI") From 83284d8b3ecede9dcde8459eafcb925f02894dfd Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Thu, 20 Feb 2025 17:14:48 -0500 Subject: [PATCH 4/9] remove benchmark variant --- packages/rn-tester/android/app/build.gradle.kts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/packages/rn-tester/android/app/build.gradle.kts b/packages/rn-tester/android/app/build.gradle.kts index a023c06356d6..21790e324c1d 100644 --- a/packages/rn-tester/android/app/build.gradle.kts +++ b/packages/rn-tester/android/app/build.gradle.kts @@ -62,7 +62,7 @@ react { /* Hermes Commands */ // The hermes compiler command to run. By default it is 'hermesc' hermesCommand = "$reactNativeDirPath/ReactAndroid/hermes-engine/build/hermes/bin/hermesc" - enableHermesOnlyInVariants = listOf("hermesDebug", "hermesRelease", "hermesBenchmark") + enableHermesOnlyInVariants = listOf("hermesDebug", "hermesRelease") autolinkLibrariesWithApp() } @@ -142,11 +142,6 @@ android { proguardFiles(getDefaultProguardFile("proguard-android.txt")) signingConfig = signingConfigs.getByName("debug") } - create("benchmark") { - initWith(buildTypes.getByName("release")) - matchingFallbacks += listOf("release") - isDebuggable = false - } } sourceSets.named("main") { // SampleTurboModule. @@ -207,9 +202,6 @@ afterEvaluate { tasks .getByName("createBundleHermesReleaseJsAndAssets") .dependsOn(":packages:react-native:ReactAndroid:hermes-engine:buildHermesC") - tasks - .getByName("createBundleHermesBenchmarkJsAndAssets") - .dependsOn(":packages:react-native:ReactAndroid:hermes-engine:buildHermesC") } // As we're building 4 native flavors in parallel, there is clash on the `.cxx/Debug` and @@ -233,10 +225,4 @@ afterEvaluate { tasks .getByName("createBundleHermesReleaseJsAndAssets") .dependsOn(":packages:react-native:ReactAndroid:buildCodegenCLI") - tasks - .getByName("createBundleJscBenchmarkJsAndAssets") - .dependsOn(":packages:react-native:ReactAndroid:buildCodegenCLI") - tasks - .getByName("createBundleHermesBenchmarkJsAndAssets") - .dependsOn(":packages:react-native:ReactAndroid:buildCodegenCLI") } From 3834a078b0d5710f526d47a21d5572f2b1be8412 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 21 Feb 2025 10:49:19 -0500 Subject: [PATCH 5/9] Review feedback --- .../react-native/gradle/libs.versions.toml | 24 ++++++++++--------- .../benchmark/src/main/AndroidManifest.xml | 1 - .../benchmark/ExampleStartupBenchmark.kt | 13 +++------- .../rn-tester/android/app/build.gradle.kts | 2 +- settings.gradle.kts | 4 ++-- 5 files changed, 19 insertions(+), 25 deletions(-) delete mode 100644 packages/rn-tester/android/app/benchmark/src/main/AndroidManifest.xml diff --git a/packages/react-native/gradle/libs.versions.toml b/packages/react-native/gradle/libs.versions.toml index ba283e5ec061..3f61fcde4008 100644 --- a/packages/react-native/gradle/libs.versions.toml +++ b/packages/react-native/gradle/libs.versions.toml @@ -10,12 +10,15 @@ agp = "8.8.0" androidx-annotation = "1.6.0" androidx-appcompat = "1.7.0" androidx-autofill = "1.1.0" +androidx-profileinstaller = "1.4.1" androidx-swiperefreshlayout = "1.1.0" androidx-test = "1.5.0" androidx-tracing = "1.1.0" assertj = "3.21.0" +benchmarkMacroJunit4 = "1.3.3" binary-compatibility-validator = "0.13.2" download = "5.4.0" +espressoCore = "3.6.1" fbjni = "0.7.0" fresco = "3.6.0" infer-annotation = "0.18.0" @@ -24,6 +27,7 @@ javax-inject = "1" jsc-android = "2026004.0.1" jsr305 = "3.0.2" junit = "4.13.2" +junitVersion = "1.2.1" kotlin = "2.0.21" mockito = "3.12.4" mockito-kotlin = "3.2.0" @@ -32,6 +36,7 @@ okhttp = "4.9.2" okio = "2.9.0" robolectric = "4.9.2" soloader = "0.12.1" +uiautomator = "2.3.0" xstream = "1.4.20" yoga-proguard-annotations = "1.19.0" # Native Dependencies @@ -42,20 +47,21 @@ fmt="11.0.2" folly="2024.11.18.00" glog="0.3.5" gtest="1.12.1" -junitVersion = "1.2.1" -espressoCore = "3.6.1" -uiautomator = "2.3.0" -benchmarkMacroJunit4 = "1.3.3" [libraries] +androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-appcompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "androidx-appcompat" } -androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" } androidx-autofill = { module = "androidx.autofill:autofill", version.ref = "androidx-autofill" } +androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmarkMacroJunit4" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidx-profileinstaller" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefreshlayout" } -androidx-tracing = { module = "androidx.tracing:tracing", version.ref = "androidx-tracing" } -androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test" } androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" } +androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test" } +androidx-tracing = { module = "androidx.tracing:tracing", version.ref = "androidx-tracing" } +androidx-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } fbjni = { module = "com.facebook.fbjni:fbjni", version.ref = "fbjni" } fresco = { module = "com.facebook.fresco:fresco", version.ref = "fresco" } @@ -80,10 +86,6 @@ mockito = {module = "org.mockito:mockito-inline", version.ref = "mockito" } mockito-kotlin = {module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockito-kotlin" } robolectric = {module = "org.robolectric:robolectric", version.ref = "robolectric" } thoughtworks = {module = "com.thoughtworks.xstream:xstream", version.ref = "xstream" } -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } -androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } -androidx-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } -androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmarkMacroJunit4" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/packages/rn-tester/android/app/benchmark/src/main/AndroidManifest.xml b/packages/rn-tester/android/app/benchmark/src/main/AndroidManifest.xml deleted file mode 100644 index 227314eeb7de..000000000000 --- a/packages/rn-tester/android/app/benchmark/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt b/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt index 5b2c045ebd65..4bbd3279c989 100644 --- a/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt +++ b/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt @@ -18,17 +18,10 @@ import org.junit.Test import org.junit.runner.RunWith /** - * This is an example startup benchmark. + * RNTester benchmarks. * - * It navigates to the device's home screen, and launches the default activity. - * - * Before running this benchmark: - * 1) switch your app's active build variant in the Studio (affects Studio runs only) - * 2) add `` to your app's manifest, within the `` - * tag - * - * Run this benchmark from Studio to see startup measurements, and captured system traces for - * investigating your app's performance. + * Run this benchmark from Android Studio to see startup measurements, and captured system traces for + * investigating performance. */ @RunWith(AndroidJUnit4::class) class ExampleStartupBenchmark { diff --git a/packages/rn-tester/android/app/build.gradle.kts b/packages/rn-tester/android/app/build.gradle.kts index 21790e324c1d..a4db0dbb417d 100644 --- a/packages/rn-tester/android/app/build.gradle.kts +++ b/packages/rn-tester/android/app/build.gradle.kts @@ -165,7 +165,7 @@ dependencies { "jscImplementation"(jscFlavor) testImplementation(libs.junit) - implementation("androidx.profileinstaller:profileinstaller:1.4.1") + implementation(libs.androidx.profileinstaller) } android { diff --git a/settings.gradle.kts b/settings.gradle.kts index 8d22d7f38c63..46f192cc11bd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,7 +18,8 @@ include( ":packages:react-native:ReactAndroid", ":packages:react-native:ReactAndroid:hermes-engine", ":packages:react-native:ReactAndroid:external-artifacts", - ":packages:rn-tester:android:app") + ":packages:rn-tester:android:app", + ":packages:rn-tester:android:app:benchmark") includeBuild("packages/gradle-plugin/") @@ -40,4 +41,3 @@ configure { workingDirectory = file("packages/rn-tester/"), lockFiles = files("yarn.lock")) } -include(":packages:rn-tester:android:app:benchmark") From b8ebf6a3f10e8a0319d817cbdcfa3681430b903e Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 21 Feb 2025 10:51:11 -0500 Subject: [PATCH 6/9] Fix whitespace --- settings.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 46f192cc11bd..ce06b908ac73 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,4 +40,3 @@ configure { autolinkLibrariesFromCommand( workingDirectory = file("packages/rn-tester/"), lockFiles = files("yarn.lock")) } - From cd0ee784b441b802341172c9b53a7b1182ee7ec9 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Tue, 25 Feb 2025 21:06:59 -0500 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Nicola Corti --- packages/react-native/gradle/libs.versions.toml | 12 ++++++------ .../rn-tester/android/app/benchmark/build.gradle.kts | 6 +++--- .../com/example/benchmark/ExampleStartupBenchmark.kt | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/react-native/gradle/libs.versions.toml b/packages/react-native/gradle/libs.versions.toml index 3f61fcde4008..e936fdc45801 100644 --- a/packages/react-native/gradle/libs.versions.toml +++ b/packages/react-native/gradle/libs.versions.toml @@ -10,15 +10,16 @@ agp = "8.8.0" androidx-annotation = "1.6.0" androidx-appcompat = "1.7.0" androidx-autofill = "1.1.0" +androidx-benchmark-macro-junit4 = "1.3.3" androidx-profileinstaller = "1.4.1" androidx-swiperefreshlayout = "1.1.0" androidx-test = "1.5.0" +androidx-test-junit = "1.2.1" androidx-tracing = "1.1.0" assertj = "3.21.0" -benchmarkMacroJunit4 = "1.3.3" binary-compatibility-validator = "0.13.2" download = "5.4.0" -espressoCore = "3.6.1" +espresso-core = "3.6.1" fbjni = "0.7.0" fresco = "3.6.0" infer-annotation = "0.18.0" @@ -27,7 +28,6 @@ javax-inject = "1" jsc-android = "2026004.0.1" jsr305 = "3.0.2" junit = "4.13.2" -junitVersion = "1.2.1" kotlin = "2.0.21" mockito = "3.12.4" mockito-kotlin = "3.2.0" @@ -53,9 +53,9 @@ androidx-annotation = { module = "androidx.annotation:annotation", version.ref = androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-appcompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "androidx-appcompat" } androidx-autofill = { module = "androidx.autofill:autofill", version.ref = "androidx-autofill" } -androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmarkMacroJunit4" } -androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidx-benchmark-macro-junit4" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" } androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidx-profileinstaller" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefreshlayout" } androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" } diff --git a/packages/rn-tester/android/app/benchmark/build.gradle.kts b/packages/rn-tester/android/app/benchmark/build.gradle.kts index d52ac7196534..74dc723aa0fd 100644 --- a/packages/rn-tester/android/app/benchmark/build.gradle.kts +++ b/packages/rn-tester/android/app/benchmark/build.gradle.kts @@ -12,11 +12,11 @@ plugins { android { namespace = "com.example.benchmark" - compileSdk = 35 + compileSdk = libs.versions.compileSdk.get().toInt() defaultConfig { - minSdk = 24 - targetSdk = 35 + minSdk = libs.versions.minSdk.get().toInt() + targetSdk = libs.versions.targetSdk.get().toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt b/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt index 4bbd3279c989..46b64bd77254 100644 --- a/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt +++ b/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt @@ -24,7 +24,7 @@ import org.junit.runner.RunWith * investigating performance. */ @RunWith(AndroidJUnit4::class) -class ExampleStartupBenchmark { +class RNTesterStartupBenchmark { @get:Rule val benchmarkRule = MacrobenchmarkRule() @Test From 1cf060c5137dc62f0e380d1b11bf606fa3178d00 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 26 Feb 2025 10:09:31 -0500 Subject: [PATCH 8/9] Fix import --- .../ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js b/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js index 8050560d0eb7..232e9c07643d 100644 --- a/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js +++ b/packages/rn-tester/ReportFullyDrawnView/ReportFullyDrawnViewNativeComponent.js @@ -8,8 +8,8 @@ * @format */ +import type {HostComponent} from 'react-native'; import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; -import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; From 9f9e4aad081bf1d5010aa2cc3a11d7b93d023971 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 28 Feb 2025 11:43:22 -0500 Subject: [PATCH 9/9] Fix benchmark package, move ReportFullyDrawn view --- .../benchmark/RNTesterStartupBenchmark.kt} | 2 +- packages/rn-tester/js/RNTesterAppShared.js | 69 +++++++++---------- 2 files changed, 35 insertions(+), 36 deletions(-) rename packages/rn-tester/android/app/benchmark/src/main/java/com/{example/benchmark/ExampleStartupBenchmark.kt => facebook/react/uiapp/benchmark/RNTesterStartupBenchmark.kt} (96%) diff --git a/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt b/packages/rn-tester/android/app/benchmark/src/main/java/com/facebook/react/uiapp/benchmark/RNTesterStartupBenchmark.kt similarity index 96% rename from packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt rename to packages/rn-tester/android/app/benchmark/src/main/java/com/facebook/react/uiapp/benchmark/RNTesterStartupBenchmark.kt index 46b64bd77254..cf49d461fbcd 100644 --- a/packages/rn-tester/android/app/benchmark/src/main/java/com/example/benchmark/ExampleStartupBenchmark.kt +++ b/packages/rn-tester/android/app/benchmark/src/main/java/com/facebook/react/uiapp/benchmark/RNTesterStartupBenchmark.kt @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -package com.example.benchmark +package com.facebook.react.uiapp.benchmark import androidx.benchmark.macro.StartupMode import androidx.benchmark.macro.StartupTimingMetric diff --git a/packages/rn-tester/js/RNTesterAppShared.js b/packages/rn-tester/js/RNTesterAppShared.js index 91e10fd56de4..ef69b0b2fc45 100644 --- a/packages/rn-tester/js/RNTesterAppShared.js +++ b/packages/rn-tester/js/RNTesterAppShared.js @@ -266,42 +266,41 @@ const RNTesterApp = ({ screen === Screens.COMPONENTS ? examplesList.components : examplesList.apis; return ( - - - - {activeModule && BackButtonComponent ? ( - - ) : undefined} - - - {activeModule != null ? ( - - ) : ( - - )} - - - + + {activeModule && BackButtonComponent ? ( + + ) : undefined} + + + {activeModule != null ? ( + - - - + ) : ( + + )} + + + + + + ); };