Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Repository with Store4 [ja]

Repository with Store4 [ja]

Avatar for Daichi Furiya (Wasabeef)

Daichi Furiya (Wasabeef)

January 17, 2020
Tweet

More Decks by Daichi Furiya (Wasabeef)

Other Decks in Technology

Transcript

  1. そして、MVVM で設 計する上で、使いや すいライブラリが Jetpack には多く存 在します。 Architecture(MVVM) Activity/Fragment Repository

    ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack
  2. そして、MVVM で設 計する上で、使いや すいライブラリが Jetpack には多く存 在します。 Architecture(MVVM) Activity/Fragment Repository

    ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack
  3. そして、MVVM で設 計する上で、使いや すいライブラリが Jetpack には多く存 在します。 Architecture(MVVM) Activity/Fragment Repository

    ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack
  4. そして、MVVM で設 計する上で、使いや すいライブラリが Jetpack には多く存 在します。 Architecture(MVVM) Activity/Fragment Repository

    ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack Jetpack Jetpack
  5. ただ、Repository の実装については参 考にできるもの (iosched, plaid) はあれど Jetpack で はライブラリ化され ていません。

    Architecture(MVVM) Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack Jetpack Jetpack ?????
  6. Repository pattern? Activity/Fragment Repository ViewModel LiveData Local Source Room Remote

    Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Store4 そこで現在は Dropbox が主体と なって開発されてい る Store4 を使うこ とで、簡単になりそ うなので今回注目し ています。
  7. Android の MVVM に合 わせて要約すると、 Repository を使うこと で、ローカルから取得す るかネットワークから取 得するかを

    ViewModel が意識する必要がないよ うにします。 Repository pattern? Activity/Fragment Repository ViewModel LiveData Remote Data Source Retrofit/okHttp Local Source Room https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Store4
  8. 当たり前のことですが Repository を意識しな くていいのは ViewModel から使う側 の話で、実装において は意識して書かなけれ ばなりません。 Repository

    pattern? Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData Store4
  9. Repository pattern? Activity/Fragment Repository ViewModel LiveData Remote Data Source Retrofit/okHttp

    https://developer.android.com/jetpack/docs/guide LiveData On Memory Store4 さらにいうと、Local Source が SQLite だけ ではなくその前に On Memory に保存・取得す る仕組みを考えだすと、 実装さらに大変になって いきます。 Local Source Room
  10. 例えば、同じ画面に複 数の Fragment がい て、それぞれ User データを取得するため に UserRepository か

    ら getUser をコールし たとします。 よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C
  11. よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C そうすると、

    UserRepository がシ ングルトンだとして も、それぞれがネット ワーク通信をして、 データを取得しにいき ます。
  12. build.gradle !// Set the source & target compatibilities to 1.8

    android { compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 } !!... } dependencies { !// 2020.1.17 implementation 'com.dropbox.mobile.store:store4:4.0.0-alpha01' }
  13. StoreBuilder typealias UserName = String typealias UserId = String typealias

    TimeMs = Long data class User( val id: UserId, val name: UserName, val birthday: TimeMs) val userId: UserId = "wasabeef" UserId を Key として Store で取得するイメージで簡単な説明をしていきます。
  14. StoreBuilder val store = StoreBuilder !!... Repository に相当する Store を

    StoreBuilder で実装していきます。 これは、DI で管理することになるかと思います。
  15. StoreBuilder val store = StoreBuilder .fromNonFlow<UserId, User> { key !->

    } fromNonFlow<Key, Output> に 最新のデータを取得するためメソッドコールを定義します。
  16. StoreBuilder val store = StoreBuilder .fromNonFlow<UserId, User> { key !->

    api.fetchUser(key).user } ここでは、Retrofit など Data source を使って API コールします。
  17. StoreBuilder val store = StoreBuilder .from<UserId, User> { key !->

    api.fetchUser(key) } ここでは、Data source が既に Flow 化されているのであれば fromNonFlow {} !-> from {} ͱ͍ͯͩ͘͠͞ɻ
  18. StoreBuilder val store = StoreBuilder .fromNonFlow<UserId, User> { key !->

    api.fetchUser(key).user }.persister( reader = !!..., writer = !!..., delete = !!..., ).build() データを永続化するときにどういう振る舞いを行うかを Persister として指定することができます。
  19. StoreBuilder val store = StoreBuilder .fromNonFlow<UserId, User> { key !->

    api.fetchUser(key).user }.persister( reader = db.userDao()!::load, writer = db.userDao()!::update, delete = db.userDao()!::clear ).build() もちろん Room で使うことも出来ます。
  20. StoreBuilder val store = StoreBuilder .fromNonFlow<UserId, User> { key !->

    api.fetchUser(key).user }.build() 導入初期は Persister を指定しないで、On Memory キャッシュとしてだけ 使うのを試してもいいかもしれません。
  21. suspend fun Store.get(key) !// UserViewModel.kt viewModelScope.launch { userLiveData.value = try

    { val user = store.get("wasabeef") Result.Success(user) } catch(e: Exception) { Result.Error(e) } } StoreBuilder の Store 実装が終わっていれば、get() を使ってデータを取得します。 これは、サスペンド関数なので ViewModel の CoroutineScope 内などで行います。
  22. suspend fun Store.fresh(key) !// UserViewModel.kt viewModelScope.launch { userLiveData.value = try

    { val user = store.fresh("wasabeef") Result.Success(user) } catch(e: Exception) { Result.Error(e) } } fresh() を使ってデータを取得することもできます。
  23. get or fresh ? get(key) 指定されたキーを元にメモリ内または ディスクキャッシュから取得 fresh(key) キャッシュを無視して最新を取得 get

    と fresh をどちらを使うかを意識しないといけないのは Repository pattern 概念としては難しいところがあるかもしれませんが … Pull-to-Refresh などの仕組みがある以上、必要なことだと思います。
  24. MemoryPolicy デフォルトのメモリポリシーでは上限 100 個、期限 24 時間となっています。 class MemoryPolicy internal constructor(

    val expireAfterWrite: Long, val expireAfterAccess: Long, val expireAfterTimeUnit: TimeUnit, private val maxSizeNotDefault: Long ) { !// expireAfterWrite = TimeUnit.HOURS.toSeconds(24), !// expireAfterTimeUnit = TimeUnit.SECONDS, !// maxSizeNotDefault = 100 }