victoralbertos / reactivecache Goto Github PK
View Code? Open in Web Editor NEWA reactive cache for Android and Java which honors the reactive chain.
License: Apache License 2.0
A reactive cache for Android and Java which honors the reactive chain.
License: Apache License 2.0
When exception is thrown from Observable/Single... and there is not data in the cache - initial exception is swallowed making it's hard to debug possible issue.
Right now when I have:
Loader.load(config)
.subscribeOn(Schedulers.io())
.map(params -> {
...
return map;
})
.subscribe(
this::foo,
e -> {
Log.e(TAG, "Unable to execute loader", e);
}
);
If the exception is thrown from Loader.load()
and there is not data in the cache - the original exception is swallowed and replaced with:
io.rx_cache2.RxCacheException: The Loader provided did not return any data and there is not data to load from the Cache home
at io.rx_cache2.internal.ProcessorProvidersBehaviour$5.apply(ProcessorProvidersBehaviour.java:148)
at io.reactivex.internal.operators.observable.ObservableOnErrorReturn$OnErrorReturnObserver.onError(ObservableOnErrorReturn.java:73)
at io.reactivex.internal.observers.BasicFuseableObserver.onError(BasicFuseableObserver.java:100)
at io.reactivex.internal.disposables.EmptyDisposable.error(EmptyDisposable.java:63)
at io.reactivex.internal.operators.observable.ObservableError.subscribeActual(ObservableError.java:37)
at io.reactivex.Observable.subscribe(Observable.java:10955)
...
Caused by: io.reactivecache2.ExceptionAdapter$PlaceHolderLoader
at io.reactivecache2.ExceptionAdapter.placeholderLoader(ExceptionAdapter.java:42)
at io.reactivecache2.Provider.lambda$read$3(Provider.java:71)
at io.reactivecache2.Provider$$Lambda$3.call(Unknown Source)
at io.reactivex.internal.operators.single.SingleDefer.subscribeActual(SingleDefer.java:36)
at io.reactivex.Single.subscribe(Single.java:2779)
...
It makes sense for debugging purposes to add original exception as the cause.
How to evictAll for ProviderGroup?
providerGroup.evict().subscribe(); <- not works
reactiveCache.evictAll().subscribe(); <- works, but for all providers
Kind of the next steps of: VictorAlbertos/RxCache#60
Many operations are returning the wrong observable type. The api would be way more meaningful if the correct types are returned. I.e
method | current | expected |
---|---|---|
evict | observable | completable |
read | observable | single |
readNullable | observable | single |
Probably, you should evict all groups cache here when pull to refresh
Observable<List<Event>> events(boolean pullToRefresh, int page) {
if (pullToRefresh) {
// evict all caches, not only for the current page
return apiEvents.events(page)
.compose(cacheProvider.replace(page));
}
class EventsRepository {
private final ProviderGroup<List<Event>> cacheProvider;
private final ApiEvents apiEvents;
EventsRepository(ApiEvents apiEvents, ReactiveCache reactiveCache) {
this.apiEvents = apiEvents;
this.cacheProvider = reactiveCache.<List<Event>>providerGroup()
.withKey("events");
}
Observable<List<Event>> events(boolean pullToRefresh, int page) {
if (pullToRefresh) {
return apiEvents.events(page)
.compose(cacheProvider.replace(page));
}
return apiEvents.events(page)
.compose(cacheProvider.readWithLoader(page));
}
}
After debugging like forever I found out why the load on one of the backend routes is significantly larger.
I think I found the issue. If the cache type is a List<WhatEver>
and an empty list is returned no dataClassName
can be determined and thus the cache does not work.
Hey Victor, first of all, thanks for the great lib !
I'm struggling finding out how to use the replace (Provider) in my app.
I have this:
@Override
public Observable<List<Food>> fetchFoodList() {
return Observable.create(subscriber -> {
Backendless.Data.of(Food.class).find(new AsyncCallback<BackendlessCollection<Food>>() {
@Override
public void handleResponse(BackendlessCollection<Food> response) {
subscriber.onNext(response.getCurrentPage());
}
@Override
public void handleFault(BackendlessFault fault) {
subscriber.onError(new Exception(fault.getMessage()));
}
});
}).compose(provider.replace());
}
My provider:
private final Provider<List<Food>> provider;
this.provider = reactiveCache.<List<Food>>provider().withKey("food");
But I'm getting:
error: method compose in class Observable<T> cannot be applied to given types;
}).compose(provider.replace());
^
required: Transformer<? super Object,? extends R>
found: ObservableTransformer<List<Food>,List<Food>>
reason: cannot infer type-variable(s) R
(argument mismatch; ObservableTransformer<List<Food>,List<Food>> cannot be converted to Transformer<? super Object,? extends R>)
where R,T are type-variables:
R extends Object declared in method <R>compose(Transformer<? super T,? extends R>)
T extends Object declared in class Observable
Note: /Users/leonardo/Food/app/src/main/java/com/leonardo2204/food/address_pick/AddressPickController.java uses unchecked or unsafe operations.
What I am doing wrong ?
I got following exception/crash when updating dagger2 to the new version.
It might be cause by this issue. And looks very similar to issue from another project
AndroidRuntime E FATAL EXCEPTION: main
E Process: com.spotme.android, PID: 4817
E java.lang.NoSuchMethodError: No static method injectMembers(Ldagger/MembersInjector;Ljava/lang/Object;)Ljava/lang/Object; in class Ldagger/internal/Me
mbersInjectors; or its super classes (declaration of 'dagger.internal.MembersInjectors' appears in base.apk!classes29.dex)
E at io.rx_cache2.internal.cache.EvictRecord_Factory.get(EvictRecord_Factory.java:36)
E at io.rx_cache2.internal.cache.EvictRecord_Factory.get(EvictRecord_Factory.java:11)
E at io.rx_cache2.internal.cache.TwoLayersCache_Factory.get(TwoLayersCache_Factory.java:33)
E at io.rx_cache2.internal.cache.TwoLayersCache_Factory.get(TwoLayersCache_Factory.java:7)
E at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
E at io.rx_cache2.internal.ProcessorProvidersBehaviour_Factory.get(ProcessorProvidersBehaviour_Factory.java:48)
E at io.rx_cache2.internal.ProcessorProvidersBehaviour_Factory.get(ProcessorProvidersBehaviour_Factory.java:11)
E at io.rx_cache2.internal.RxCacheModule_ProvideProcessorProvidersFactory.get(RxCacheModule_ProvideProcessorProvidersFactory.java:30)
E at io.rx_cache2.internal.RxCacheModule_ProvideProcessorProvidersFactory.get(RxCacheModule_ProvideProcessorProvidersFactory.java:8)
E at io.rx_cache2.internal.DaggerRxCacheComponent.providers(DaggerRxCacheComponent.java:202)
E at io.reactivecache2.ReactiveCache.<init>(ReactiveCache.java:39)
E at io.reactivecache2.ReactiveCache.<init>(ReactiveCache.java:30)
E at io.reactivecache2.ReactiveCache$Builder.using(ReactiveCache.java:154)
Hi @VictorAlbertos ,
evict/evictAll is not working for me.Any idea why?
Referenced from RxCache: VictorAlbertos/RxCache#57
Hello, I am trying to enable encryption but when I do the cache is not working and constantly loads data from the network.
What am I doing:
@Provides @Singleton fun provideCache(@ApplicationContext context: Context): ReactiveCache =
ReactiveCache.Builder()
.encrypt("test")
.useExpiredDataWhenNoLoaderAvailable()
.using(context.filesDir, MoshiSpeaker())
And for the provider:
private val cacheProvider: ProviderList<Ingredient> = reactiveCache.providerList<Ingredient>()
.encrypt(true)
.lifeCache(1, TimeUnit.DAYS)
.withKey("ingredients")
I tried to debug a bit in case I was doing something wrong and I noticed that during Disk.retrieveRecord()
the File
passed to jolyglot
was not properly decrypted and as a result the json parsing was failing with:
java.lang.RuntimeException: com.squareup.moshi.JsonEncodingException: Use JsonReader.setLenient(true) to accept malformed JSON at path $
I am attaching a screenshot of the MoshiSpeaker.fromJson
where it shows that the BufferedSource
passed to JsonReader
is not valid.
Can you please help?
Thanks a lot!!
I'm using your library. I'm testing new tools in Android Studio 3 Canary 1. We've got finally possibility to use Java8 built-in features without using for example retrolamda library.
My app is crashing on ReactiveCache using AS3 Java8 built-in features.
Is retrolambda dependency for this library mandatory?
java.lang.NoSuchMethodError: No static method lambda$replace$2(Lio/reactivecache2/Provider;Lio/reactivex/Single;)Lio/reactivex/SingleSource; in class Lio/reactivecache2/Provider; or its super classes (declaration of 'io.reactivecache2.Provider' appears in /data/app/.../split_lib_dependencies_apk.apk)
at io.reactivecache2.Provider$$Lambda$2.apply(Unknown Source)
at io.reactivex.Single.compose(Single.java:1548)
Not sure right now if this is AS3 bug or ReactiveCache. I'm still investingating this.
It doesn't make any change in the behavior of the ReactiveCache, whether you add useExpiredDataWhenNoLoaderAvailabe()
or not.
Whenever data expires, the record will be removed from both memory and disk. (regardless of using useExpiredDataWhenNoLoaderAvailabe()
)
The code:
customerProvider = new ReactiveCache.Builder() .useExpiredDataWhenNoLoaderAvailable() .diskCacheSize(5) .using(context.getCacheDir(), new GsonSpeaker()) .provider() .lifeCache(15, TimeUnit.SECONDS) .withKey("CUSTOMER");
getCustomerApi.compose(customerProvider.readWithLoader());
Please provide a sample guide to use it with rxcache and retrofit. Right now not able to configure it as it is returning single and not able to compose network callback.
I'm using moshi for data serialization.
Now I added my custom Optional<T>
class I want to serialize.
However I get an IllegalArgumentException
of Platform T annotated [] requires explicit JsonAdapter to be registered
Caused by: java.lang.IllegalArgumentException: Platform T annotated [] requires explicit JsonAdapter to be registered
at com.squareup.moshi.ClassJsonAdapter$1.create(ClassJsonAdapter.java:41)
at com.squareup.moshi.Moshi.adapter(Moshi.java:94)
at com.squareup.moshi.ClassJsonAdapter$1.createFieldBindings(ClassJsonAdapter.java:81)
at com.squareup.moshi.ClassJsonAdapter$1.create(ClassJsonAdapter.java:65)
at com.squareup.moshi.Moshi.adapter(Moshi.java:94)
at com.squareup.moshi.StandardJsonAdapters$ObjectJsonAdapter.toJson(StandardJsonAdapters.java:322)
at com.squareup.moshi.JsonAdapter$1.toJson(JsonAdapter.java:75)
at com.squareup.moshi.ClassJsonAdapter$FieldBinding.write(ClassJsonAdapter.java:204)
at com.squareup.moshi.ClassJsonAdapter.toJson(ClassJsonAdapter.java:173)
at com.squareup.moshi.JsonAdapter$1.toJson(JsonAdapter.java:75)
at com.squareup.moshi.JsonAdapter.toJson(JsonAdapter.java:44)
at com.squareup.moshi.JsonAdapter.toJson(JsonAdapter.java:50)
at io.victoralbertos.jolyglot.MoshiSpeaker.toJson(MoshiSpeaker.java:58)
at io.rx_cache.internal.Disk.save(Disk.java:111)
at io.rx_cache.internal.Disk.saveRecord(Disk.java:57)
at io.rx_cache.internal.cache.SaveRecord.save(SaveRecord.java:50)
at io.rx_cache.internal.cache.TwoLayersCache.save(TwoLayersCache.java:45)
at io.rx_cache.internal.ProcessorProvidersBehaviour$7.call(ProcessorProvidersBehaviour.java:137)
at o.rx_cache.internal.ProcessorProvidersBehaviour$7.call(ProcessorProvidersBehaviour.java:119)
at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
Moshi is registered with my factory producing optionals:
public final class OptionalFactory implements JsonAdapter.Factory {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (type instanceof ParameterizedType && Optional.class == ((ParameterizedType) type).getRawType()) {
Type innerType = ((ParameterizedType) type).getActualTypeArguments()[0];
JsonAdapter a = moshi.adapter(innerType);
//noinspection unchecked
return new OptionalAdapter<>(a);
}
return null;
}
}
but type instanceof ParameterizedType
always evaluates to false.
Hi,
I'd like to show the cached data and meanwhile download the new data in the background. If there's no cached data(first run), I simply show No data available TextView. What I tried so far is this:
ReactiveCache reactiveCache = new ReactiveCache.Builder() .diskCacheSize(30) .useExpiredDataWhenNoLoaderAvailable() .using(app.getFilesDir(),new GsonSpeaker()); return reactiveCache;
cacheProvider = reactiveCache.<ContactsList>provider() .lifeCache(5,TimeUnit.SECONDS) .withKey("contacts");
I'm using it like this:
apiInteractor.getContacts() .compose(cacheProvider.readWithLoader()) .subscribeOn(Schedulers.newThread()) .observeOn(mainThread()),
When I disabled the WiFi and reloaded the fragment, I expected the cached data to be emitted but I get the RxCacheException instead. Sorry if I'm missing something, I'm quite new in Rx. I've even tried expirable(false) but with no luck.
Stack trace:
W/System.err: io.rx_cache.RxCacheException: The Loader provided did not return any data and there is not data to load from the Cache contacts W/System.err: at io.rx_cache.internal.ProcessorProvidersBehaviour$6.call(ProcessorProvidersBehaviour.java:156) W/System.err: at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$1.call(OperatorOnErrorResumeNextViaFunction.java:53) W/System.err: at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$1.call(OperatorOnErrorResumeNextViaFunction.java:50) W/System.err: at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140) W/System.err: at rx.internal.operators.OnSubscribeMap$MapSubscriber.onError(OnSubscribeMap.java:88) W/System.err: at retrofit2.adapter.rxjava.OperatorMapResponseToBodyOrError$1.onError(OperatorMapResponseToBodyOrError.java:52) W/System.err: at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:178) W/System.err: at rx.internal.producers.ProducerArbiter.setProducer(ProducerArbiter.java:126) W/System.err: at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.setProducer(OperatorOnErrorResumeNextViaFunction.java:159) W/System.err: at rx.internal.operators.OnSubscribeMap$MapSubscriber.setProducer(OnSubscribeMap.java:102) W/System.err: at rx.Subscriber.setProducer(Subscriber.java:205) W/System.err: at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152) W/System.err: at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138) W/System.err: at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) W/System.err: at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) W/System.err: at rx.Observable.unsafeSubscribe(Observable.java:10200) W/System.err: at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) W/System.err: at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) W/System.err: at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) W/System.err: at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) W/System.err: at rx.Observable.unsafeSubscribe(Observable.java:10200) W/System.err: at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) W/System.err: at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) W/System.err: at rx.Observable.unsafeSubscribe(Observable.java:10200) W/System.err: at rx.internal.operators.OperatorMerge$MergeSubscriber.onNext(OperatorMerge.java:248) W/System.err: at rx.internal.operators.OperatorMerge$MergeSubscriber.onNext(OperatorMerge.java:148) W/System.err: at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:77) W/System.err: at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:77) W/System.err: at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276) W/System.err: at rx.Subscriber.setProducer(Subscriber.java:211) W/System.err: at rx.internal.operators.OnSubscribeMap$MapSubscriber.setProducer(OnSubscribeMap.java:102) W/System.err: at rx.internal.operators.OnSubscribeMap$MapSubscriber.setProducer(OnSubscribeMap.java:102) W/System.err: at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138) W/System.err: at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129) W/System.err: at rx.Observable.unsafeSubscribe(Observable.java:10200) W/System.err: at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) W/System.err: at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) W/System.err: at rx.Observable.unsafeSubscribe(Observable.java:10200) W/System.err: at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) W/System.err: at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) W/System.err: at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) W/System.err: at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) W/System.err: at rx.Observable.unsafeSubscribe(Observable.java:10200) W/System.err: at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) W/System.err: at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) W/System.err: at rx.Observable.unsafeSubscribe(Observable.java:10200) W/System.err: at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94) W/System.err: at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) W/System.err: at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:442) W/System.err: at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:137) W/System.err: at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:150) W/System.err: at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:264) W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) W/System.err: at java.lang.Thread.run(Thread.java:856) W/System.err: Caused by: java.net.UnknownHostException: Unable to resolve host
.compose(cacheProvider.readWithLoaderAsReply())
this take long long and long time to get caching data
any solution ??
How to read Event
item lets say, with key ? In readme, i can only see cacheProvider.read()
which does not take key. As i understand, there are two keys? in this library, one for cacheProvider
like "event", another for item
itself like "1243". Is that correct
Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
Could not resolve com.github.VictorAlbertos:ReactiveCache:1.1.3-2.+.
Required by:
project :app
project :app > project :data
...
> Failed to list versions for com.github.VictorAlbertos:ReactiveCache.
> Unable to load Maven meta-data from https://jitpack.io/com/github/VictorAlbertos/ReactiveCache/maven-metadata.xml.
> Could not HEAD 'https://jitpack.io/com/github/VictorAlbertos/ReactiveCache/maven-metadata.xml'. Received status code 401 from server: Unauthorized
@VictorAlbertos I know that this has been answered before but I would like you to elaborate a bit on it again.
Although I have enabled the useExpiredDataWhenNoLoaderAvailabe
when I launch the app the ProcessorProvidersBehaviour
starts the EvictExpiredRecordsPersistence
and my expired data are being evicted and thus, I get the RxCacheException: The Loader provided did not return any data and there is not data to load from the Cache
error.
I think that this is misleading since I would expect to always have data in my cache (since I have loaded them at least once of course) and just refresh them whenever possible. So I think it would be better to not evict expired records on app launch when useExpiredDataWhenNoLoaderAvailabe
.
Is there a way to get the age of a cache from a provider? I would like to do something like using cache when within 24 hours, or when request fails.
When using your lib, it give me warnings when try reading from cache:
W/System.err: java.io.IOException: last block incomplete in decryption
W/System.err: at javax.crypto.CipherOutputStream.close(CipherOutputStream.java:138)
W/System.err: at io.rx_cache.internal.encrypt.BuiltInEncryptor.write(BuiltInEncryptor.java:106)
W/System.err: at io.rx_cache.internal.encrypt.BuiltInEncryptor.decrypt(BuiltInEncryptor.java:62)
W/System.err: at io.rx_cache.internal.encrypt.FileEncryptor.decrypt(FileEncryptor.java:52)
W/System.err: at io.rx_cache.internal.Disk.retrieveRecord(Disk.java:213)
W/System.err: at io.rx_cache.internal.cache.EvictExpiredRecordsPersistence.startEvictingExpiredRecords(EvictExpiredRecordsPersistence.java:46)
W/System.err: at io.rx_cache.internal.ProcessorProvidersBehaviour$1.call(ProcessorProvidersBehaviour.java:59)
W/System.err: at io.rx_cache.internal.ProcessorProvidersBehaviour$1.call(ProcessorProvidersBehaviour.java:57)
W/System.err: at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
W/System.err: at rx.internal.operators.OperatorMerge$MergeSubscriber.emitScalar(OperatorMerge.java:517)
W/System.err: at rx.internal.operators.OperatorMerge$MergeSubscriber.tryEmit(OperatorMerge.java:471)
W/System.err: at rx.internal.operators.OperatorMerge$MergeSubscriber.onNext(OperatorMerge.java:246)
...
Here is the code I used to create ReactiveCache:
ReactiveCache reactiveCache = new ReactiveCache.Builder()
.using(application.getFilesDir(), new GsonSpeaker());
You can see that I didn't use encrypt(String)
config.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.