fkm blog

software開発に関することを書いていきます

Google I/O 2019: What's New in Architecture Components

youtu.be

2年前のI/Oで、Archtecture Componentとして LifecycleRoom が発表された。その後ページングが追加され、去年のI/OではナビゲーションとWorkManagerが追加された。

プロの開発者の70%が、これらのうち1つ以上をアプリにいれて使っている。

開発サイクル

まず、EAPで一部の開発者に向けて公開し、みんなに公開していいと判断したタイミングでアルファ版になる。改善の結果、ベータ版でAPIが固定される。その後stableになり、次のアルファ版フェーズが始まる。

f:id:fkm:20190602131629p:plain

Kotlin first

ktxを出すだけでなく、Kotlinしか使えない機能も。APIのデザインもkotlinにあわせる。

Data Binding

ライブラリの中でも古い方のData Binding、Android Studio 3.5でコンパイルが早くなった(20%ほど)。

インクリメンタルなアノテーションプロセッシングを使いたい場合は、次の行を build.gradle に追加する。

android.databinding.incremental = true

また、クラス生成がライブになった。例えばレイアウトXMLでidを追加したとき、即座にクラス側も変更されるようになった。

リファクタリングにも対応した。例えばレイアウトXMLであるクラスのメソッドを呼んでいたとする。今まではそのメソッド名をリファクタリング機能で変更したとき、レイアウトXML側は変わらなかったがAndroid Studio 3.5から変更されるようになった。

Data Bindingでのエラーもよりわかりやすくなった。

View Binding

Android Studio 3.6で登場予定(もうCanaryが出てるので触ってみよう)。

  • Gradle pluginでBinding classを生成するよ
  • 100% コンパイルタイムsafety
  • Android Studioへのインテグレーションばっちり(要検証
  • Data Bindingとのコンパチ

ViewModelとSavedState

ViewModelはあくまでメモリに情報を保存するので、プロセスが止まるようなケースだと情報がなくなってしまう。一方SavedStateはシステム側に情報を退避させるので、中断+プロセス断からの再開でも情報を復元できる。メモリをたくさん消費するようなオブジェクトはViewModelに、Viewの状態(チェックがついているかどうかや、スクロール位置とか)はSavedStateに。

ここで登場するのが SavedStateHandle、これはViewModelのコンストラクタで受け取り、ViewModelの中で使う。SavedStateHandle は単なるmap-likeオブジェクト。

val handle: SavedStateHandle

// 読み込み
val myValue: Int = handle.get("key")

// 書き込み
handle.put("key", newValue)

LiveData も扱える。mutableになる。

val liveData = handle.getLiveDate("key")
liveData.observe(lifecycleOwner) { value -> ... }
liveData.value = newValue

ViewModelの初期化は ktx 使うと次のようにかける。

val userViewMode: UserViewModel by viewModels()

WorkManager

オンデマンドでの初期化はアルファ版のWorkManagerで使える。初期化は今まではアプリの先頭だったが、これからは必要とする時点で初期化できる。

カスタムのApplicationクラスを次のようにする。

class MyApp: Application(), Configuration.Provider {
    override fun getWorkManagerConfiguration(): Configuration {
        return Configuration.Builder()
            // 設定をここに書く
            .build()
    }
}

使う時は、 getInstance()context を渡す。

WorkManager.getInstance(context).enqueue(...)

Android M以前の端末用に、Google Play Servicesとの連携も用意中(coming soon)

アプリを強制停止した時などの動作は、実は端末間によって差がある。この差をなんとかしようとしてるみたい。

WorkManagerのテストにRobolectricが使えるようになった。

Workの単体テストはアルファ版で使える。テスト用のワーカーを TestWorkerBuilderTestListenalbeWorkerBuilder を使って作る。そして doWork()startWork() を呼んでテストする。

val request = OneTimeWorkRequestBuilder<MyWorker>.build()
val worker = TestWorkerBuilder.from(context, request, executor).build()

val result = worker.doWork()
assertThat(result, `is`(Result.success()))
...

Listenableなワーカーは、こんな感じ。

val request = OneTimeWorkRequestBuilder<MyWorker>.build()
val worker = TestListenableWorkerBuilder.from(context, request).build()

val result = worker.startWork().get()
assertThat(result, `is`(Result.success()))
...

WorkManagerのForeground service対応はやっている最中。

Room

Room 2.1から、DAOのメソッドに suspend をつけることができるようになった。

全文検索にも対応した。Entityに @Fts4 をつけるだけ。そしてDAOでは次のように MATCH が使える。

@Dao
interface SongDAO {
    @Query("""
        SELECT * 
        FROM Song
        WHERE Song MATCH :query
    """)
    fun searchSongs(query: String): List<Song>
}

データベースのViewにも対応した。

@DatabaseView("""
    SELECT
        Album *,
        count(song_id) AS num_of_songs,
        sum(song_elapsed_time) as total_time
    FROM Album
    JOIN AlbumSongRef ON (album_id = ref_alubm_id)
    JOIN Song ON (ref_song_id = song_id)
    GROUP BY album_id
""")
data class AlbumItem(
    @Embedded
    val album: Album
    @ColumnName("num_of_songs)
    val numOfSongs: Int
    @ColumnName("total_time")
    val totalTime: Long
)

RxJavaも対応。DAOの戻り値の型に CompletableSingle<Int> とかが使えるように。

Paging

今後の課題。

  • ネットワークエラーへの対応
  • ヘッダーとフッター

Navigation

次期バージョンでは、 - ViewModelスコープの対応。 - URIでの遷移。 - 遷移先にダイアログ - dynamic featureの対応

udacityに最新のAndroidアプリ開発コースができてる。https://www.udacity.com/course/ud9012