Pembukaan kembali Grand Store: memuat data ke Android menggunakan coroutine

Saya memberi perhatian Anda terjemahan dari artikel asli dari Mike Nakhimovich


Sekali waktu, saya bekerja di New York Times dan membuat perpustakaan bernama Store, yang merupakan "perpustakaan Java untuk memuat data yang mudah dan responsif." Kami menciptakan Store menggunakan RxJavatemplat yang diambil dari implementasi Cache Guava . Pengguna aplikasi saat ini mengharapkan pembaruan data terjadi di UI tanpa harus melakukan hal-hal seperti tarik-untuk-menyegarkanuntuk menyegarkan data atau berpindah antar layar bolak-balik. Frontend reaktif membuat saya berpikir tentang bagaimana kita dapat memiliki gudang data deklaratif dengan API sederhana yang berfungsi abstrak seperti pelambatan multitask dan cache disk, yang diperlukan dalam aplikasi mobile modern. Dalam tiga tahun operasinya, Store memiliki 45 kontributor dan lebih dari 3.500 bintang GitHub. Mulai sekarang, saya senang mengumumkan bahwa Dropbox akan melakukan pengembangan aktif Store dan akan merilisnya sepenuhnya di Kotlin dengan dukungan untuk Coroutinesdan Flow. Dan sekarang Store 4, ini adalah kesempatan untuk mengambil keuntungan dari apa yang telah kita pelajari dengan memikirkan kembali API dan kebutuhan ekosistem Android saat ini.




Android . . , , Google. , Android. Android Jetpack:



, Android Jetpack :



https://developer.android.com/jetpack/docs/guide


  • :  , AndroidX , Lifecycle Scopes . , view- .
  • View Model Live Data:  , , ( , !).
  • Room:  SQLite, ORM RxJava .
  • Remote Data Source:  Jetpack, Retrofit Okhttp Square .

, , , ( , , Jetpack ;-) ). , . , Dropbox Store, , .


, Store, . ,


«… , , .  , , , ».

. , , , . , .


Store?


, , Store, Android- . — , . , UX. , , . , . Store, .


, , . …



Store :



Store


, , Store, , , .


4 – Android . , . , Room SQLDelight, , . -, push-, Firebase, «» . Store , , , , , . , Store: http://github.com/dropbox/Store.


Store 4 . RxJava , Flow.


, , , , RxJava? . , , . , . , , , , .


— . RxJava - . API RxJava :


// Observable.java
@CheckReturnValue
public final Disposable subscribe(Consumer onNext) {}

, , RxJava Observable Disposable . dispose(), . . RxJava2 @CheckReturnValue— , flowable.subscribe , . , , . , RxJava, .


RxJava , dispose(). . RxJava, , , Kotlin Flow , . , Flow . Flow , .


suspend fun Flow.collect(...)

Collect RxJava, , Flow. RxJava, collect() "suspend". , ( async/await), . Flow.collect , , .


, Android ( ) , , . , Kotlin, , viewModelScope Jetpack, , . , Android: , .


public fun CoroutineScope.launch(...)
 
viewModelScope.launch {
  flow.collect{ handle(it) }
}

, Flow. Android. , AndroidX . , ViewModel, , Room Flow. , (Paging). Store , Android , .


, Store , Android, , , , Kotlin multi-platform, . RxJava Kotlin native/js 6000 .


Store?


Store . Store, Fetcher, , , . , Store . Store Flow, — ! Store // , , .


Store


, Store. , :


StoreBuilder.fromNonFlow { api.fetchSubreddit(it, "10")}
            .persister(
              reader = db.postDao()::loadPosts,
              writer = db.postDao()::insertPosts,
              delete = db.postDao()::clearFeed)
            .cachePolicy(MemoryPolicy)
            .build()

:


  • ,
  • API , , .

Store  . persister() Store. , Observable, Jetpack Room, SQLDelight Realm.


:


Store



 Store  . , Flow, , ReturnType.


val store = StoreBuilder.from {
                articleId -> api.getArticle(articleId) //Flow<Article>
            }
            .build()

Store , , toString(), equals() hashCode(). Fetcher . . , equals() hashcode() data- Kotlin.


: Stream



API, Store, :


fun stream(request: StoreRequest<Key>):Flow<StoreResponse>Output>>

StoreRequest, , . StoreResponse. StoreResponse — Kotlin, Loading, Data Error. StoreResponse ResponseOrigin, , .



  • Loading ResponseOrigin. .
  • Data , , Store.
  • Error , , ResponseOrigin.

Store , StoreResponse.Error, Flow , , . - , render/updateUI . . :


lifecycleScope.launchWhenStarted {
  store.stream(StoreRequest.cached(key = key, refresh=true)).collect { response ->
    when(response) {
        is StoreResponse.Loading -> showLoadingSpinner()
        is StoreResponse.Data -> {
            if (response.origin == ResponseOrigin.Fetcher) hideLoadingSpinner()
            updateUI(response.value)
        }
        is StoreResponse.Error -> {
            if (response.origin == ResponseOrigin.Fetcher) hideLoadingSpinner()
            showError(response.error)
        }
    }
  }
}

: Store.get(key)Store.stream(key)  Store.fresh(key).


  • suspend fun Store.get(key: Key): Value
 — . , .
  • suspend fun Store.fresh(key: Key): Value
 — , .
  • suspend fun Store.stream(key: Key): Flow
 — (Flow) .

get():


lifecycleScope.launchWhenStarted {
  val article = store.get(key)
  updateUI(article)
}

store.get(key), ( ), . store.get(key) , . ( ) , . , , , Store. Store . Store , , .



 store.fresh(key)  ( ). ,  fresh()  ,  store.get()/stream()  .  fresh()  pull-to-refresh .
 fresh()  get()  .


Stream



, store.stream(key), , , / . stream() , .


lifecycleScope.launchWhenStarted { 
  store.stream(StoreRequest.cached(3, refresh = false)) 
.collect{ }
 
 
store.stream(StoreRequest.get(3)) // ,   
    .collect{  }




, Store . , , , . , . Store.get() 12 . , . .




 Store ,  persister()  . , , Store , , .



persister(),  Flow  Store  . , ,  Store,   Store.


, Observable (Jetpack Room, SQLDelight Realm), , , UX.


StoreBuilder.fromNonFlow {api.fetchSubreddit(it, "10")}
            .persister(
              reader = db.postDao()::loadPosts,
              writer = db.postDao()::insertPosts,
              delete = db.postDao()::clearFeed)
            .cachePolicy(MemoryPolicy)
            .build()

Store , . , Store (Realm, SQLite, Firebase ..). SQLite Room, Jetpack.


API Store — , . :


  • , Room
  • (StoreRequest)
  • (stream)
  • API, Kotlin.


, Store. , , Android, . , -, - . , , .


KotlinConf:





Store : github.com/dropbox/Store




, , Android Open Source Project, , Creative Commons 2.5 Attribution License


All Articles