기준 프로필이란?
앱이 자주 실행하는 코드 경로를 미리 정의한 프로파일 파일입니다.
프로필에 포함된 경로에서 20~30% 체감 성능 개선이 이전에 비해 보고되어 앱 시작, 화면 간 이동, 콘텐츠 스크롤과 같은 모든 사용자 상호작용이 처음 실행될 때 부터 더 원할해진다.
주로 앱이 시작 될때 핵심 경로나 기능에 적용해서 사용자로 하여금 빨리 앱이 실행되게 느껴지도록 유도한다.
이 뿐만 아니라 체감상 크고 중요한 기능에도 적용될 수 있다.
@RunWith(AndroidJUnit4::class)
@LargeTest
//앱 시작을 위한 기준 프로필 생성할 수 있는 테스트 클래스 만든다.
//프로필 생성을 위한 하나의 테스트 메서드 포함
class BaselineProfileGenerator {
@get:Rule
val rule = BaselineProfileRule()
@Test
fun generate() {
// The application id for the running build variant is read from the instrumentation arguments.
rule.collect("com.example.baselineprofiles_codelab") {
// This block defines the app's critical user journey. Here we are interested in
// optimizing for app startup. But you can also navigate and scroll through your most important UI.
// Start default activity for your app
pressHome()
startActivityAndWait() //앱의 첫번째 프레임이 렌더링될 때까지 기다린다
// TODO Write more interactions to optimize advanced journeys of your app. //생성된 클래스에는 앱의 고급 경험 최적화하기 위해 추가 작성가능
// For example:
// 1. Wait until the content is asynchronously loaded
waitForAsyncContent()
// 2. Scroll the feed content
scrollSnackListJourney()
// 3. Navigate to detail screen
goToSnackDetailJourney()
// Check UiAutomator documentation for more information how to interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
}
}
/// 비동기 콘텐츠 대기
//피드 스낵 목록 찾는다 -> 목록 내의 일부 항목 화면에 표시될때까지 기다린다
fun MacrobenchmarkScope.waitForAsyncContent() {
device.wait(Until.hasObject(By.res("snack_list")), 10_000)
val contentList = device.findObject(By.res("snack_list"))
// Wait until a snack collection item within the list is rendered
contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}
// 스크롤 목록 경험
//스낵 목록 UI 요소를 찾습니다.
//시스템 탐색을 트리거하지 않도록 동작 여백을 설정합니다.
//목록을 스크롤하고 UI가 안정될 때까지 기다립니다.
fun MacrobenchmarkScope.scrollSnackListJourney() {
val snackList = device.findObject(By.res("snack_list"))
// Set gesture margin to avoid triggering gesture navigation.
snackList.setGestureMargin(device.displayWidth / 5)
snackList.fling(Direction.DOWN)
device.waitForIdle()
}
//세부정보 경험으로 이동
//클릭 → NavController.navigate() → 상세 컴포저블 빌드 → ViewModel 초기화/데이터 로드 → recomposition 까지의 연쇄 호출 경로가 실제로 실행되며 그 메서드 시그니처들이 프로필에 기록됩니다.
fun MacrobenchmarkScope.goToSnackDetailJourney() {
val snackList = device.findObject(By.res("snack_list"))
val snacks = snackList.findObjects(By.res("snack_item"))
// Select snack from the list based on running iteration.
val index = (iteration ?: 0) % snacks.size
snacks[index].click()
// Wait until the screen is gone = the detail is shown.
device.wait(Until.gone(By.res("snack_list")), 5_000)
}
앱 실행시 snack_list 렌더링, 스크롤, 상세 이동 등의 동작을 미리 컴파일 해두기 위해 기준 프로필을 정의했다.
SnackCollection(
snackCollection = snackCollection,
onSnackClick = onSnackClick,
index = index,
modifier = Modifier.testTag("snack_collection")
)
UI요소에 testTag로 snack_collection을 미리 지정해두었기에 Baseline Profile이 compose 컴포넌트를 찾아서 조작할 수 있다.
BaselineProfileGenerator 테스트 클래스에서 정의한 여러 인터랙션을 실행한 후 Android Studio에 결과를 baseline-prof.txt 형태로 제공한다.
이렇게 만든 release 배포 AAB 혹은 APK로 사용자가 앱을 설치하거나 업데이트를 하게 되면 기준 프로필도 함께 설치되어 앱을 처음 실행할 때부터 성능이 향상된다.
실제 앱의 성능에 효과를 발휘하는지 확인해야 한다.
앱 시작 시간을 기준 프로필 사용할 때와 하지 않을 때로 나누어서 비교하기위해 벤치마크 클래스를 만든다.
class StartupBenchmarks {
@get:Rule
val rule = MacrobenchmarkRule()
@Test
fun startupCompilationNone() =
benchmark(CompilationMode.None())
@Test
fun startupCompilationBaselineProfiles() =
benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
private fun benchmark(compilationMode: CompilationMode) {
// The application id for the running build variant is read from the instrumentation arguments.
rule.measureRepeated(
packageName = InstrumentationRegistry.getArguments().getString("targetAppId")
?: throw Exception("targetAppId not passed as instrumentation runner arg"),
metrics = listOf(StartupTimingMetric()),
compilationMode = compilationMode,
startupMode = StartupMode.COLD,
iterations = 10,
setupBlock = {
pressHome()
},
measureBlock = {
startActivityAndWait()
// TODO Add interactions to wait for when your app is fully drawn.
// The app is fully drawn when Activity.reportFullyDrawn is called.
// For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
// from the AndroidX Activity library.
// Check the UiAutomator documentation for more information on how to
// interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
)
}
}
시작 성능 개선되었는지 여부를 측정하기 위해 앱을 사전에 컴파일하지 않는 경우, 컴파일 하는 경우 두가지 경우로 나눠서 개선 여부를 측정합니다.
주의!
성능 측정은 앱의 실제 실행 시간, CPU 사용량, 렌더링 타이밍을 측정하기 때문에 애뮬레이터가 아닌 실기기 사용을 권장합니다.
앱을 사전에 컴파일 하지 않는 경우 JIT 컴파일을 사전에 실행해야 하므로 성능이 좋지 않습니다. 기준 프로필 적용한 경우가 성능이 더 나은데 이는 기준 프로필이 있는 부분 컴파일이 사용자가 사용할 가능성이 가장 높은 코드를 AOT 컴파일하고 중요하지 않은 코드는 사전 컴파일되지 않은 채로 두므로 즉시 로드하지 않아도 되기 때문입니다.