开源软件名称:VictorAlbertos/RxCache开源软件地址:https://github.com/VictorAlbertos/RxCache开源编程语言:Java 100.0%开源软件介绍:RxCacheFor 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<List<Mock>> getMocks(Observable<List<Mock>> oMocks); SetupAdd 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'
} UsageDefine an interface Providers {
@ProviderKey("mocks")
Observable<List<Mock>> getMocks(Observable<List<Mock>> oMocks);
@ProviderKey("mocks-5-minute-ttl")
@LifeCache(duration = 5, timeUnit = TimeUnit.MINUTES)
Observable<List<Mock>> getMocksWith5MinutesLifeTime(Observable<List<Mock>> oMocks);
@ProviderKey("mocks-evict-provider")
Observable<List<Mock>> getMocksEvictProvider(Observable<List<Mock>> oMocks, EvictProvider evictProvider);
@ProviderKey("mocks-paginate")
Observable<List<Mock>> getMocksPaginate(Observable<List<Mock>> oMocks, DynamicKey page);
@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);
} RxCache exposes RxCache accepts as argument a set of classes to indicate how the provider needs to handle the cached data:
Supported annotations:
Build an instance of Providers and use itFinally, instantiate the Providers File cacheDir = getFilesDir();
Providers providers = new RxCache.Builder()
.persistence(cacheDir, new GsonSpeaker())
.using(Providers.class); Putting It All Togetherinterface 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);
} public class Repository {
private final Providers providers;
public Repository(File cacheDir) {
providers = new RxCache.Builder()
.persistence(cacheDir, new GsonSpeaker())
.using(Providers.class);
}
public Observable<List<Mock>> getMocks(final boolean update) {
return providers.getMocksEvictProvider(getExpensiveMocks(), new EvictProvider(update));
}
public Observable<List<Mock>> getMocksPaginate(final int page, final boolean update) {
return providers.getMocksPaginateEvictingPerPage(getExpensiveMocks(), new DynamicKey(page), new EvictDynamicKey(update));
}
public Observable<List<Mock>> getMocksWithFiltersPaginate(final String filter, final int page, final boolean updateFilter) {
return providers.getMocksPaginateWithFiltersEvictingPerFilter(getExpensiveMocks(), new DynamicKeyGroup(filter, page), new EvictDynamicKey(updateFilter));
}
//In a real use case, here is when you build your observable with the expensive operation.
//Or if you are making http calls you can use Retrofit to get it out of the box.
private Observable<List<Mock>> getExpensiveMocks() {
return Observable.just(Arrays.asList(new Mock("")));
}
} Use cases
Classic API RxCache:Following use cases illustrate some common scenarios which will help to understand the usage of ListList without evicting: Observable<List<Mock>> getMocks(Observable<List<Mock>> oMocks); List evicting: Observable<List<Mock>> getMocksEvictProvider(Observable<List<Mock>> oMocks, EvictProvider evictProvider);
//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 FilteringList 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);
//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 filtersList 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);
//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 The above examples declare providers which their method signature accepts 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 Nevertheless, there are complete examples for Android and Java projects. Actionable API RxCache:Limitation: This actionable API only support 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 scopes and lists. In order to use this actionable api, first you need to add the repository compiler as a dependency to your project using an annotation processor. For Android, it would be as follows: Add this line to your root build.gradle: dependencies {
// other classpath definitions here
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
} Then make sure to apply the plugin in your app/build.gradle and add the compiler dependency: apply plugin: 'com.neenbedankt.android-apt'
dependencies {
// apt command comes from the android-apt plugin
apt "com.github.VictorAlbertos.RxCache:compiler:1.8.3-2.x"
} After this configuration, every provider annotated with @Actionable The order in the params supplies must be as in the following example: public interface RxProviders {
@Actionable
Observable<List<Mock.InnerMock>> mocks(Observable<List<Mock.InnerMock>> message, EvictProvider evictProvider);
@Actionable
Observable<List<Mock>> mocksDynamicKey(Observable<List<Mock>> message, DynamicKey dynamicKey, EvictDynamicKey evictDynamicKey);
@Actionable
Observable<List<Mock>> mocksDynamicKeyGroup(Observable<List<Mock>> message, DynamicKeyGroup dynamicKeyGroup, EvictDynamicKeyGroup evictDynamicKey);
} The observable value must be a The previous RxProviders RxProvidersActionable.mocks(RxProviders proxy);
RxProvidersActionable.mocksDynamicKey(RxProviders proxy, DynamicKey dynamicKey);
RxProvidersActionable.mocksDynamicKeyGroup(RxProviders proxy, DynamicKeyGroup dynamicKeyGroup); These methods return an instance of the Some actions examples: ActionsProviders.mocks(rxProviders)
.addFirst(new Mock())
.addLast(new Mock())
//Add a new mock at 5 position
.add((position, count) -> position == 5, new Mock())
.evictFirst()
//Evict first element if the cache has already 300 records
.evictFirst(count -> count > 300)
.evictLast()
//Evict last element if the cache has already 300 records
.evictLast(count -> count > 300)
//Evict all inactive elements
.evictIterable((position, count, mock) -> mock.isInactive())
.evictAll()
//Update the mock with id 5
.update(mock -> mock.getId() == 5, mock -> {
mock.setActive();
return mock;
})
//Update all inactive mocks
.updateIterable(mock -> mock.isInactive(), mock -> {
mock.setActive();
return mock;
})
.toObservable()
.subscribe(processedMocks -> {}) Every one of the previous actions will be execute only after the composed observable receives a subscription. This way, the underliyng provider cache will be modified its elements without effort at all. MigrationsRxCache provides a simple mechanism for handling migrations between releases. You need to annotate your providers @SchemeMigration({
@Migration(version = 1, evictClasses = {Mock.class}),
@Migration(version = 2, evictClasses = {Mock2.class}),
@Migration(version = 3, evictClasses = {Mock3.class})
})
interface Providers {} You want to annotate a new migration only when a new field has been added in a class model used by RxCache. Deleting classes or deleting fields of classes would be handle automatically by RxCache, so you don't need to annotate a new migration when a field or an entire class has been deleted. For instance: A migration was added at some point. After that, a second one was added eventually. @SchemeMigration({
@Migration(version = 1, evictClasses = {Mock.class}),
@Migration(version = 2, evictClasses = {Mock2.class})
})
interface Providers {} But now @SchemeMigration({
@Migration(version = 2, evictClasses = {Mock2.class})
})
interface Providers {} Because RxCache has an internal process to clean memory when it is required, the data will be evicted eventually. EncryptionRxCache provides a simple mechanism to encrypt the data. You need to annotate your providers Important: If the value of the @EncryptKey("myStrongKey-1234")
interface Providers {
@Encrypt
Observable<List<Mock>> getMocksEncrypted(Observable<List<Mock>> oMocks);
Observable<List<Mock>> getMocksNotEncrypted(Observable<List<Mock>> oMocks);
} Configure general behaviourRxCache allows to set certain parameters when building the providers instance: Configure the limit in megabytes for the data to be persistedBy default, RxCache sets the limit in 100 megabytes, but you can change this value by calling setMaxMBPersistenceCache method when building the provider instance. new RxCache.Builder()
.setMaxMBPersistenceCache(maxMgPersistenceCache)
.persistence(cacheDir)
.using(Providers.class); This limit ensure that the disk will no grow up limitless in case you have providers with dynamic keys which values changes dynamically, like filters based on gps location or dynamic filters supplied by your back-end solution. When this limit is reached, RxCache will not be able to persist in disk new data. That's why RxCache has an automated process to evict any record when the threshold memory assigned to the persistence layer is close to be reached, even if the record life time has not been fulfilled. But provider's record annotated with @Expirable annotation and |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论