Grand reopening of the Store: loading data into Android using coroutine

I bring to your attention a translation of the original article from Mike Nakhimovich


Once upon a time, I worked at the New York Times and created a library called Store, which was "the Java library for easy, responsive data loading." We created the Store using RxJavatemplates taken from the implementation of Guava's Cache . Today’s app users expect data updates to happen in the UI without having to do things like pull-to-refreshto refresh data or move between screens back and forth. The reactive frontend made me think about how we can have declarative data warehouses with simple APIs that abstract complex functions like multitask throttling and disk caching, which are necessary in modern mobile applications. In its three years of operation, Store has 45 contributors and more than 3,500 GitHub stars. From now on, I am pleased to announce that Dropbox will take on the active development of the Store and will release it fully on Kotlin with support for Coroutinesand Flow. And now Store 4, this is an opportunity to take advantage of what we have learned by rethinking the API and current needs of the Android ecosystem.




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