はじめに
こんにちは、ブランドソリューション開発本部フロントエンド部WEAR Androidブロックの安土琢朗です。普段はファッションコーディネートアプリWEARのAndroidアプリを開発しています。
WEARではすでにXMLで書かれたレイアウトをJetpack Composeにリファクタリングする作業を進めています。作業を進める中で、Jetpack ComposeのLazyColumn利用箇所でスクロールが以前よりスムーズに動かない、初回起動時にスクロールが遅いなどのパフォーマンス問題に直面しました。
本投稿では、最適なパフォーマンスを実現する方法の1つであるベースラインプロファイルの導入の仕方について説明します。
ベースラインプロファイルとは
ベースラインプロファイルはAndroid Runtime(ART)がプリコンパイルする時に使うクラスやメソッドをリスト化してあるものです。ベースラインプロファイルを使うことで起動時間とジャンクの削減、全体的なランタイムパフォーマンスの向上ができます。
それでは実際に導入をみていきましょう。
導入方法
benchmarkモジュールをアプリに追加する
まずAndroid Studioの"Create New Module" でベンチマーク用のモジュールを追加するテンプレートが用意されているのでモジュールを追加します。
ベースライン プロファイルの難読化を無効にする
ベンチマークに対して難読化を無効にする必要があります。appモジュール内にbenchmark-rules.proというファイルを作成します。
- benchmark-rules.pro
# Disables obfuscation for benchmark builds.
-dontobfuscate
次に、appモジュールのbuild.gradle.ktsでbenchmark buildTypeを変更し、作成したファイルを追加します。
- app/build.gradle.kts
buildTypes {
create("benchmark") {
signingConfig = signingConfigs.getByName("debug")
matchingFallbacks += listOf("release")
proguardFiles("benchmark-rules.pro")
}
}
ベースラインプロファイルのジェネレータを作成する
ベースラインプロファイルを生成するために、benchmarkモジュールにテストクラスBaselineProfileGeneratorを作成します。
- BaselineProfileGenerator.kt
@ExperimentalBaselineProfilesApi
class BaselineProfileGenerator {
companion object {
const val PACKAGE_NAME = "com.example.myapplication"
}
@get:Rule
val baselineProfileRule = BaselineProfileRule()
@Test
fun generate() =
baselineProfileRule.collectBaselineProfile(PACKAGE_NAME) {
startActivityAndWait()
startApplication()
scrollScreen()
}
// アプリを起動する関数
private fun MacrobenchmarkScope.startApplication() {
pressHome()
startActivityAndWait()
device.wait(Until.hasObject(By.pkg(device.launcherPackageName).depth(0)), 5_000)
val suggestions = device.findObject(By.res("suggestions"))
val searchCondition = Until.hasObject(By.res("coordinateTop"))
suggestions.wait(searchCondition, 5_000)
}
// リストをスクロールする関数
private fun MacrobenchmarkScope.scrollScreen() {
val suggestions = device.findObject(By.res("suggestions"))
suggestions.setGestureMargin(device.displayWidth / 5)
suggestions.fling(Direction.DOWN)
device.waitForIdle()
}
}
startApplication() 関数では次の3点を行います。
- アプリの状態が再起動になったことを確認。
- デフォルトのアクティビティを開始し、最初のフレームがレンダリングされるのを待つ。
- コンテンツが読み込まれてレンダリングされ、ユーザー操作が可能になるまで待つ。
scrollScreen()関数では次の2点を行います。
- LazyColumnのmodifierにtestTagを追加してtagを元にスクロールできるUI要素を見つける。
- リストをスクロールする。
ベースラインプロファイルのジェネレータを実行する
ベースラインプロファイルを生成するには、root権限のあるAndroid9(API 28)以上のデバイスを使用する必要があります。benchmarkモジュールのbuild.gradle.ktsファイルで、Gradleで管理されているデバイスを定義します。
- benchmark/build.gradle.kts
testOptions {
managedDevices {
devices {
create<ManagedVirtualDevice>("pixel2Api31") {
device = "Pixel 2"
apiLevel = 31
systemImageSource = "aosp"
}
}
}
}
生成されたベースラインプロファイルをアプリに適用する
テストが正常に終了したら、アプリにベースラインプロファイルを適用します。生成されたファイルは/benchmark/build/outputs/の中のmanaged_device_android_test_additional_output/フォルダ内にあります。そのファイルをappモジュールにbaseline-prof.txtでコピーします。
続いて、appモジュールにprofileinstallerの依存関係を追加します。
- app/build.gradle.kts
dependencies {
implementation("androidx.profileinstaller:profileinstaller:1.2.0")
}
ここまでがベースラインプロファイルを生成してアプリに適用するまでの手順です。
次に実際にアプリのパフォーマンスについて測定してみます。使うライブラリはMacrobenchmarkです。
Macrobenchmarkとは
「Jetpack Macrobenchmark」は、パフォーマンスを測定するために導入されます。起動やUIの操作、アニメーションなどのパフォーマンスを測定できます。このライブラリを使用すると、以下のことができます。
- アプリを複数回測定し、起動パターンやスクロール速度で測定できます。
- 複数のテスト実行結果を平均化し、パフォーマンスのばらつきを抑えることができます。
- アプリのコンパイル状態を制御することで、パフォーマンスの安定性に影響を与える要因を制御できます。
- Google Playストアで行われるインストール時の最適化をローカルで再現して、実際のパフォーマンスを確認できます。
Macrobenchmark導入方法
Macrobenchmarkのライブラリを追加
まずbenchmarkモジュールにMacrobenchmarkのライブラリを追加します。
続きはこちら