Reactive caching library for Android and Java
:warning: This repository is no longer mantained consider using Room as an alternative :warning:
For a more reactive approach go here.
The goal of this library is simple: caching your data models like Picasso caches your images, with no effort at all.
Every Android application is a client application, which means it does not make sense to create and maintain a database just for caching data.
Plus, the fact that you have some sort of legendary database for persisting your data does not solves by itself the real challenge: to be able to configure your caching needs in a flexible and simple way.
Inspired by Retrofit api, RxCache is a reactive caching library for Android and Java which turns your caching needs into an interface.
When supplying an observable, single, maybe or flowable (these are the supported Reactive types) which contains the data provided by an expensive task -probably an http connection, RxCache determines if it is needed
to subscribe to it or instead fetch the data previously cached. This decision is made based on the providers configuration.
Observable<List<Mock>> getMocks(Observable<List<Mock>> oMocks);Add the JitPack repository in your build.gradle (top level module):
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}And add next dependencies in the build.gradle of the module:
dependencies {
compile "com.github.VictorAlbertos.RxCache:runtime:1.8.3-2.x"
compile "io.reactivex.rxjava2:rxjava:2.1.6"
}Because RxCache uses internally Jolyglot to serialize and deserialize objects, you need to add one of the next dependency to gradle.
dependencies {
// To use Gson
compile 'com.github.VictorAlbertos.Jolyglot:gson:0.0.4'
// To use Jackson
compile 'com.github.VictorAlbertos.Jolyglot:jackson:0.0.4'
// To use Moshi
compile 'com.github.VictorAlbertos.Jolyglot:moshi:0.0.4'
}Define an interface with as much methods as needed to create the caching providers:
…RxCache exposes evictAll() method to evict the entire cache in a row.
RxCache accepts as argument a set of classes to indicate how the provider needs to handle the cached data:
EvictDynamicKey.EvictDynamicKeyGroup.Supported annotations:
@LifeCache is not supplied, the data will be never evicted unless it is required explicitly using EvictProvider, EvictDynamicKey or EvictDynamicKeyGroup .Finally, instantiate the Providers interface using RxCache.Builder and supplying a valid file system path which would allow RxCache to write on disk.
File cacheDir = getFilesDir();
Providers providers = new RxCache.Builder()
.persistence(cacheDir, new GsonSpeaker())
.using(Providers.class);interface Providers {
@ProviderKey("mocks-evict-provider")
Observable<List<Mock>> getMocksEvictProvider(Observable<List<Mock>> oMocks, EvictProvider evictProvider);
@ProviderKey("mocks-paginate-evict-per-page")
Observable<List<Mock>> getMocksPaginateEvictingPerPage(Observable<List<Mock>> oMocks, DynamicKey page, EvictDynamicKey evictPage);
@ProviderKey("mocks-paginate-evict-per-filter")
Observable<List<Mock>> getMocksPaginateWithFiltersEvictingPerFilter(Observable<List<Mock>> oMocks, DynamicKeyGroup filterPage, EvictDynamicKey evictFilter);
}…Following use cases illustrate some common scenarios which will help to understand the usage of DynamicKey and DynamicKeyGroup classes along with evicting scopes.
List without evicting:
Observable<List<Mock>> getMocks(Observable<List<Mock>> oMocks);List evicting:
Observable<List<Mock>> getMocksEvictProvider(Observable<List<Mock>> oMocks, EvictProvider evictProvider);Runtime usage:
//Hit observable evicting all mocks
getMocksEvictProvider(oMocks, new EvictProvider(true))
//This line throws an IllegalArgumentException: "EvictDynamicKey was provided but not was provided any DynamicKey"
getMocksEvictProvider(oMocks, new EvictDynamicKey(true))List filtering without evicting:
Observable<List<Mock>> getMocksFiltered(Observable<List<Mock>> oMocks, DynamicKey filter);List filtering evicting:
Observable<List<Mock>> getMocksFilteredEvict(Observable<List<Mock>> oMocks, DynamicKey filter, EvictProvider evictDynamicKey);Runtime usage:
//Hit observable evicting all mocks using EvictProvider
getMocksFilteredEvict(oMocks, new DynamicKey("actives"), new EvictProvider(true))
//Hit observable evicting mocks of one filter using EvictDynamicKey
getMocksFilteredEvict(oMocks, new DynamicKey("actives"), new EvictDynamicKey(true))
//This line throws an IllegalArgumentException: "EvictDynamicKeyGroup was provided but not was provided any Group"
getMocksFilteredEvict(oMocks, new DynamicKey("actives"), new EvictDynamicKeyGroup(true))List paginated with filters without evicting:
Observable<List<Mock>> getMocksFilteredPaginate(Observable<List<Mock>> oMocks, DynamicKey filterAndPage);List paginated with filters evicting:
Observable<List<Mock>> getMocksFilteredPaginateEvict(Observable<List<Mock>> oMocks, DynamicKeyGroup filterAndPage, EvictProvider evictProvider);Runtime usage:
//Hit observable evicting all mocks using EvictProvider
getMocksFilteredPaginateEvict(oMocks, new DynamicKeyGroup("actives", "page1"), new EvictProvider(true))
//Hit observable evicting all mocks pages of one filter using EvictDynamicKey
getMocksFilteredPaginateEvict(oMocks, new DynamicKeyGroup("actives", "page1"), new EvictDynamicKey(true))
//Hit observable evicting one page mocks of one filter using EvictDynamicKeyGroup
getMocksFilteredPaginateInvalidate(oMocks, new DynamicKeyGroup("actives", "page1"), new EvictDynamicKeyGroup(true))As you may already notice, the whole point of using DynamicKey or DynamicKeyGroup along with Evict classes is to play with several scopes when evicting objects.
The above examples declare providers which their method signature accepts EvictProvider in order to be able to concrete more specifics types of EvictProvider at runtime.
But I have done that for demonstration purposes, you always should narrow the evicting classes in your method signature to the type which you really need. For the last example, I would use EvictDynamicKey in production code, because this way I would be able to paginate the filtered items and evict them per its filter, triggered by a pull to refresh for instance.
Nevertheless, there are complete examples for Android and Java projects.
Limitation: This actionable API only support Observable as Reactive type.
This actionable api offers an easy way to perform write operations using providers. Although write operations could be achieved using the classic api too, it's much complex and error-prone. Indeed, the Actions class it's a wrapper around the classic api which play with evicting
No open issues yet, or sync has not completed.