Publish current app and server

This commit is contained in:
ponzischeme89
2026-08-02 22:10:19 +12:00
parent a265636139
commit 1ed180c739
203 changed files with 23933 additions and 2788 deletions
+18 -6
View File
@@ -1,6 +1,7 @@
plugins {
id("com.android.test")
id("org.jetbrains.kotlin.android")
id("androidx.baselineprofile")
}
android {
@@ -8,12 +9,16 @@ android {
compileSdk = 35
defaultConfig {
minSdk = 23
// Baseline profile generation needs API 28+. The app itself still ships to 23 —
// this floor applies only to the machine generating the profile, not to the TVs
// that consume it.
minSdk = 28
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments["androidx.benchmark.enabledRules"] = "Macrobenchmark"
// The target is the current debug build for Phase 1 only. Results are labelled
// debug-influenced; a release benchmark variant will be added before Phase 2.
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE"
// No DEBUGGABLE suppression any more: the baselineprofile plugin adds
// nonMinifiedRelease / benchmarkRelease variants to :app and mirrors them here,
// so these are real release numbers rather than the debug-influenced ones the
// Phase 1 setup produced.
testInstrumentationRunnerArguments["androidx.benchmark.enabledRules"] = "Macrobenchmark,BaselineProfile"
}
targetProjectPath = ":app"
experimentalProperties["android.experimental.self-instrumenting"] = true
@@ -31,6 +36,12 @@ android {
}
}
baselineProfile {
// A television is the only device that produces a representative profile for this
// app, so generation always runs against whatever is connected over adb.
useConnectedDevices = true
}
kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
@@ -38,7 +49,8 @@ kotlin {
}
dependencies {
implementation("androidx.benchmark:benchmark-macro-junit4:1.2.4")
implementation("androidx.benchmark:benchmark-macro-junit4:1.3.4")
implementation("androidx.test.ext:junit:1.2.1")
implementation("androidx.test:runner:1.6.2")
implementation("androidx.test.uiautomator:uiautomator:2.3.0")
}
@@ -0,0 +1,51 @@
package com.ponzischeme89.memby.benchmark
import android.view.KeyEvent
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Records the classes and methods the launcher touches between process start and the
* first few D-pad moves, so ART can compile them ahead of time instead of interpreting
* them on a TV box that has nothing to spare.
*
* Run against a real television:
*
* .\gradlew.bat :app:generateReleaseBaselineProfile
*
* The output lands in app/src/release/generated/baselineProfiles and is checked in, so
* an ordinary `assembleRelease` needs no device. Regenerate it after a change that moves
* the startup path — a stale profile is not harmful, just progressively less useful.
*/
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val rule = BaselineProfileRule()
@Test
fun startupAndBrowse() = rule.collect(
packageName = "com.ponzischeme89.memby",
// The signed-in launcher renders from HomeCache before the network answers, so a
// cold start reaches real rows without waiting on the gateway. Include enough
// D-pad movement to pull in the row/card composables and Coil's decode path,
// which is where the first-scroll jank lives.
includeInStartupProfile = true,
) {
pressHome()
startActivityAndWait()
browseHome()
}
private fun MacrobenchmarkScope.browseHome() {
repeat(4) { device.pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT) }
device.waitForIdle()
device.pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN)
repeat(3) { device.pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT) }
device.waitForIdle()
device.pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN)
device.waitForIdle()
}
}
@@ -15,24 +15,35 @@ import org.junit.runner.RunWith
class HomeBenchmark {
@get:Rule val benchmarkRule = MacrobenchmarkRule()
/**
* The baseline for comparison: no ahead-of-time compilation at all. Keep this around
* — the only way to know whether [coldStartToHomeWithProfile] is earning its keep is
* to see the two numbers side by side.
*/
@Test
fun coldStartToHome() = benchmarkRule.measureRepeated(
packageName = "com.ponzischeme89.memby",
fun coldStartToHomeNoCompilation() = measureColdStart(CompilationMode.None())
/** What a viewer installing a release APK actually gets. */
@Test
fun coldStartToHomeWithProfile() = measureColdStart(CompilationMode.Partial())
private fun measureColdStart(mode: CompilationMode) = benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
metrics = listOf(StartupTimingMetric()),
compilationMode = CompilationMode.None(),
compilationMode = mode,
startupMode = StartupMode.COLD,
iterations = 3,
iterations = 5,
setupBlock = { pressHome() },
measureBlock = { startActivityAndWait() },
)
@Test
fun homeDpadAndRows() = benchmarkRule.measureRepeated(
packageName = "com.ponzischeme89.memby",
packageName = PACKAGE_NAME,
metrics = listOf(FrameTimingMetric()),
compilationMode = CompilationMode.None(),
compilationMode = CompilationMode.Partial(),
startupMode = StartupMode.WARM,
iterations = 3,
iterations = 5,
setupBlock = { startActivityAndWait() },
measureBlock = { exerciseHome() },
)
@@ -44,4 +55,8 @@ class HomeBenchmark {
device.pressKeyCode(android.view.KeyEvent.KEYCODE_DPAD_RIGHT)
device.waitForIdle()
}
private companion object {
const val PACKAGE_NAME = "com.ponzischeme89.memby"
}
}