storable bug fix + static fixes
This commit is contained in:
parent
2927215c57
commit
631cf157dd
|
|
@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
|
|||
apply plugin: 'me.tatarka.retrolambda'
|
||||
|
||||
android {
|
||||
compileSdkVersion 24
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '25.0.2'
|
||||
|
||||
compileOptions {
|
||||
|
|
@ -12,11 +12,11 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 24
|
||||
targetSdkVersion 25
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
provided 'com.android.support:support-annotations:24.2.1'
|
||||
provided 'com.android.support:support-annotations:25.2.0'
|
||||
provided 'io.reactivex:rxandroid:1.2.1'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ public class ConsoleLogProcessor extends LogProcessor {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("WrongConstant")
|
||||
//WrongConstant: level.getPriority() is not wrong constant!
|
||||
public void processLogMessage(@NonNull final LcGroup group, @NonNull final LcLevel level,
|
||||
@NonNull final String tag, @NonNull final String message, @Nullable final Throwable throwable) {
|
||||
final String messageToLog = normalize(message + (throwable != null ? '\n' + Log.getStackTraceString(throwable) : ""));
|
||||
|
|
@ -50,8 +52,9 @@ public class ConsoleLogProcessor extends LogProcessor {
|
|||
newline = newline != -1 ? newline : length;
|
||||
do {
|
||||
final int end = Math.min(newline, i + MAX_LOG_LENGTH);
|
||||
//noinspection WrongConstant
|
||||
Log.println(level.getPriority(), tag, messageToLog.substring(i, end));
|
||||
if (Log.isLoggable(tag, level.getPriority())) {
|
||||
Log.println(level.getPriority(), tag, messageToLog.substring(i, end));
|
||||
}
|
||||
i = end;
|
||||
}
|
||||
while (i < newline);
|
||||
|
|
|
|||
|
|
@ -264,7 +264,9 @@ public final class Lc {
|
|||
|
||||
public static void printStackTrace(@NonNull final String tag) {
|
||||
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
Log.d(tag, TextUtils.join("\n", Arrays.copyOfRange(stackTrace, STACK_TRACE_CODE_DEPTH, stackTrace.length)));
|
||||
if (Log.isLoggable(tag, Log.DEBUG)) {
|
||||
Log.d(tag, TextUtils.join("\n", Arrays.copyOfRange(stackTrace, STACK_TRACE_CODE_DEPTH, stackTrace.length)));
|
||||
}
|
||||
}
|
||||
|
||||
private Lc() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,212 @@
|
|||
/**
|
||||
* Copyright 2014 Netflix, Inc.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import rx.Observable.OnSubscribe;
|
||||
import rx.Scheduler;
|
||||
import rx.Subscriber;
|
||||
import rx.Subscription;
|
||||
import rx.functions.Action0;
|
||||
import rx.functions.Action1;
|
||||
import rx.observables.ConnectableObservable;
|
||||
import rx.schedulers.Schedulers;
|
||||
import rx.subscriptions.CompositeSubscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
|
||||
/**
|
||||
* Returns an observable sequence that stays connected to the source as long as
|
||||
* there is at least one subscription to the observable sequence and also it stays connected
|
||||
* for cache time after everyone unsubscribe.
|
||||
*
|
||||
* @param <T> the value type
|
||||
*/
|
||||
@SuppressWarnings({"PMD.AvoidUsingVolatile", "PMD.CompareObjectsWithEquals"})
|
||||
//AvoidUsingVolatile,CompareObjectsWithEquals: from OnSubscribeRefCount code
|
||||
public final class OnSubscribeRefCountWithCacheTime<T> implements OnSubscribe<T> {
|
||||
|
||||
@NonNull
|
||||
private final ConnectableObservable<? extends T> source;
|
||||
@NonNull
|
||||
private volatile CompositeSubscription baseSubscription = new CompositeSubscription();
|
||||
@NonNull
|
||||
private final AtomicInteger subscriptionCount = new AtomicInteger(0);
|
||||
|
||||
@NonNull
|
||||
private final Scheduler scheduler = Schedulers.computation();
|
||||
private final long cacheTime;
|
||||
@NonNull
|
||||
private final TimeUnit cacheTimeUnit;
|
||||
@Nullable
|
||||
private Scheduler.Worker worker;
|
||||
|
||||
/**
|
||||
* Use this lock for every subscription and disconnect action.
|
||||
*/
|
||||
@NonNull
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public OnSubscribeRefCountWithCacheTime(@NonNull final ConnectableObservable<? extends T> source,
|
||||
final long cacheTime, @NonNull final TimeUnit cacheTimeUnit) {
|
||||
this.source = source;
|
||||
this.cacheTime = cacheTime;
|
||||
this.cacheTimeUnit = cacheTimeUnit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(@NonNull final Subscriber<? super T> subscriber) {
|
||||
|
||||
lock.lock();
|
||||
if (subscriptionCount.incrementAndGet() == 1) {
|
||||
if (worker != null) {
|
||||
worker.unsubscribe();
|
||||
worker = null;
|
||||
}
|
||||
final AtomicBoolean writeLocked = new AtomicBoolean(true);
|
||||
|
||||
try {
|
||||
// need to use this overload of connect to ensure that
|
||||
// baseSubscription is set in the case that source is a
|
||||
// synchronous Observable
|
||||
source.connect(onSubscribe(subscriber, writeLocked));
|
||||
} finally {
|
||||
// need to cover the case where the source is subscribed to
|
||||
// outside of this class thus preventing the Action1 passed
|
||||
// to source.connect above being called
|
||||
if (writeLocked.get()) {
|
||||
// Action1 passed to source.connect was not called
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
// ready to subscribe to source so do it
|
||||
doSubscribe(subscriber, baseSubscription);
|
||||
} finally {
|
||||
// release the read lock
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Action1<Subscription> onSubscribe(@NonNull final Subscriber<? super T> subscriber,
|
||||
@NonNull final AtomicBoolean writeLocked) {
|
||||
return new Action1<Subscription>() {
|
||||
@Override
|
||||
public void call(@NonNull final Subscription subscription) {
|
||||
|
||||
try {
|
||||
baseSubscription.add(subscription);
|
||||
// ready to subscribe to source so do it
|
||||
doSubscribe(subscriber, baseSubscription);
|
||||
} finally {
|
||||
// release the write lock
|
||||
lock.unlock();
|
||||
writeLocked.set(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void doSubscribe(@NonNull final Subscriber<? super T> subscriber, @NonNull final CompositeSubscription currentBase) {
|
||||
// handle unsubscribing from the base subscription
|
||||
subscriber.add(disconnect(currentBase));
|
||||
|
||||
source.unsafeSubscribe(new Subscriber<T>(subscriber) {
|
||||
@Override
|
||||
public void onError(@NonNull final Throwable throwable) {
|
||||
cleanup();
|
||||
subscriber.onError(throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@Nullable final T item) {
|
||||
subscriber.onNext(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
cleanup();
|
||||
subscriber.onCompleted();
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
// on error or completion we need to unsubscribe the base subscription
|
||||
// and set the subscriptionCount to 0
|
||||
lock.lock();
|
||||
try {
|
||||
if (baseSubscription == currentBase) {
|
||||
baseSubscription.unsubscribe();
|
||||
baseSubscription = new CompositeSubscription();
|
||||
subscriptionCount.set(0);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Subscription disconnect(@NonNull final CompositeSubscription current) {
|
||||
return Subscriptions.create(new Action0() {
|
||||
|
||||
@Override
|
||||
public void call() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (baseSubscription == current && subscriptionCount.decrementAndGet() == 0) {
|
||||
if (worker != null) {
|
||||
worker.unsubscribe();
|
||||
} else {
|
||||
worker = scheduler.createWorker();
|
||||
}
|
||||
worker.schedule(new Action0() {
|
||||
@Override
|
||||
public void call() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (subscriptionCount.get() == 0) {
|
||||
baseSubscription.unsubscribe();
|
||||
// need a new baseSubscription because once
|
||||
// unsubscribed stays that way
|
||||
worker.unsubscribe();
|
||||
worker = null;
|
||||
baseSubscription = new CompositeSubscription();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}, cacheTime, cacheTimeUnit);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package ru.touchin.roboswag.core.observables;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 21/05/2016.
|
||||
* Some helper methods to work with JavaRx.
|
||||
*/
|
||||
public final class RxUtils {
|
||||
|
||||
/**
|
||||
* Subscribes to specific {@link Observable} and waits for it's onCompleted event
|
||||
* and then returns {@link ObservableResult} with all collected items and errors during subscription.
|
||||
* You should NOT use such method normally. It is safer than {@link Observable#toBlocking()} but it is also like a hack.
|
||||
*
|
||||
* @param observable {@link Observable} to be executed;
|
||||
* @param <T> Type of {@link Observable}'s items;
|
||||
* @return {@link ObservableResult} which contains all items and errors collected during execution.
|
||||
*/
|
||||
@NonNull
|
||||
public static <T> ObservableResult<T> executeSync(@NonNull final Observable<T> observable) {
|
||||
final ObservableResult<T> result = new ObservableResult<>();
|
||||
final CountDownLatch waiter = new CountDownLatch(1);
|
||||
observable.subscribe(result::onNext, result::onError, waiter::countDown);
|
||||
try {
|
||||
waiter.await();
|
||||
} catch (final InterruptedException exception) {
|
||||
throw new ShouldNotHappenException(exception);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private RxUtils() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,13 +24,12 @@ import android.support.annotation.Nullable;
|
|||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ru.touchin.roboswag.core.log.LcGroup;
|
||||
import ru.touchin.roboswag.core.observables.ObservableResult;
|
||||
import ru.touchin.roboswag.core.observables.RxUtils;
|
||||
import ru.touchin.roboswag.core.observables.OnSubscribeRefCountWithCacheTime;
|
||||
import ru.touchin.roboswag.core.observables.storable.builders.NonNullStorableBuilder;
|
||||
import ru.touchin.roboswag.core.utils.ObjectUtils;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Scheduler;
|
||||
|
|
@ -55,6 +54,8 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
|
||||
public static final LcGroup STORABLE_LC_GROUP = new LcGroup("STORABLE");
|
||||
|
||||
private static final long DEFAULT_CACHE_TIME_MILLIS = TimeUnit.SECONDS.toMillis(5);
|
||||
|
||||
@NonNull
|
||||
private static ObserveStrategy getDefaultObserveStrategyFor(@NonNull final Type objectType, @NonNull final Type storeObjectType) {
|
||||
if (objectType instanceof Class && ObjectUtils.isSimpleClass((Class) objectType)) {
|
||||
|
|
@ -88,18 +89,21 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
public Storable(@NonNull final BuilderCore<TKey, TObject, TStoreObject> builderCore) {
|
||||
this(builderCore.key, builderCore.objectType, builderCore.storeObjectType,
|
||||
builderCore.store, builderCore.converter, builderCore.observeStrategy,
|
||||
builderCore.migration, builderCore.defaultValue, builderCore.storeScheduler);
|
||||
builderCore.migration, builderCore.defaultValue, builderCore.storeScheduler, builderCore.cacheTimeMillis);
|
||||
}
|
||||
|
||||
public Storable(@NonNull final TKey key,
|
||||
@NonNull final Type objectType,
|
||||
@NonNull final Type storeObjectType,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter,
|
||||
@Nullable final ObserveStrategy observeStrategy,
|
||||
@Nullable final Migration<TKey> migration,
|
||||
@Nullable final TObject defaultValue,
|
||||
@Nullable final Scheduler storeScheduler) {
|
||||
@SuppressWarnings("PMD.ExcessiveParameterList")
|
||||
//ExcessiveParameterList: that's why we are using builder to create it
|
||||
private Storable(@NonNull final TKey key,
|
||||
@NonNull final Type objectType,
|
||||
@NonNull final Type storeObjectType,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter,
|
||||
@Nullable final ObserveStrategy observeStrategy,
|
||||
@Nullable final Migration<TKey> migration,
|
||||
@Nullable final TObject defaultValue,
|
||||
@Nullable final Scheduler storeScheduler,
|
||||
final long cacheTimeMillis) {
|
||||
this.key = key;
|
||||
this.objectType = objectType;
|
||||
this.storeObjectType = storeObjectType;
|
||||
|
|
@ -109,8 +113,8 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
= observeStrategy != null ? observeStrategy : getDefaultObserveStrategyFor(objectType, storeObjectType);
|
||||
scheduler = storeScheduler != null ? storeScheduler : Schedulers.from(Executors.newSingleThreadExecutor());
|
||||
storeValueObservable
|
||||
= createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue);
|
||||
valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy);
|
||||
= createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue, cacheTimeMillis);
|
||||
valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy, cacheTimeMillis);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
@ -134,7 +138,8 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
@NonNull
|
||||
private Observable<TStoreObject> createStoreValueObservable(@NonNull final ObserveStrategy observeStrategy,
|
||||
@Nullable final Migration<TKey> migration,
|
||||
@Nullable final TObject defaultValue) {
|
||||
@Nullable final TObject defaultValue,
|
||||
final long cacheTimeMillis) {
|
||||
final Observable<TStoreObject> result = (migration != null
|
||||
? migration.migrateToLatestVersion(key).subscribeOn(scheduler)
|
||||
: Completable.complete())
|
||||
|
|
@ -149,13 +154,14 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
.concatWith(newStoreValueEvent)
|
||||
.map(storeObject -> returnDefaultValueIfNull(storeObject, defaultValue));
|
||||
return observeStrategy == ObserveStrategy.CACHE_STORE_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE
|
||||
? result.replay(1).refCount()
|
||||
? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), cacheTimeMillis, TimeUnit.MILLISECONDS))
|
||||
: result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Observable<TObject> createValueObservable(@NonNull final Observable<TStoreObject> storeValueObservable,
|
||||
@NonNull final ObserveStrategy observeStrategy) {
|
||||
@NonNull final ObserveStrategy observeStrategy,
|
||||
final long cacheTimeMillis) {
|
||||
final Observable<TObject> result = storeValueObservable
|
||||
.switchMap(storeObject -> Observable
|
||||
.fromCallable(() -> converter.toObject(objectType, storeObjectType, storeObject))
|
||||
|
|
@ -169,7 +175,7 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
}
|
||||
}));
|
||||
return observeStrategy == ObserveStrategy.CACHE_ACTUAL_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE
|
||||
? result.replay(1).refCount()
|
||||
? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), cacheTimeMillis, TimeUnit.MILLISECONDS))
|
||||
: result;
|
||||
}
|
||||
|
||||
|
|
@ -223,28 +229,24 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
return converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates observable which is async setting value to store.
|
||||
* NOTE: It could emit ONLY completed and errors events. It is not providing onNext event! //TODO: it's Completable :(
|
||||
*
|
||||
* @param newValue Value to set;
|
||||
* @return Observable of setting process.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> set(@Nullable final TObject newValue) {
|
||||
return storeValueObservable
|
||||
.first()
|
||||
private Completable internalSet(@Nullable final TObject newValue, final boolean checkForEqualityBeforeSet) {
|
||||
return (checkForEqualityBeforeSet ? storeValueObservable.first() : Observable.just(null))
|
||||
.switchMap(oldStoreValue -> Observable
|
||||
.fromCallable(() -> converter.toStoreObject(objectType, storeObjectType, newValue))
|
||||
.subscribeOn(scheduler)
|
||||
.switchMap(newStoreValue -> {
|
||||
if (ObjectUtils.equals(newStoreValue, oldStoreValue)) {
|
||||
if (checkForEqualityBeforeSet && ObjectUtils.equals(newStoreValue, oldStoreValue)) {
|
||||
return Observable.empty();
|
||||
}
|
||||
return store.storeObject(storeObjectType, key, newStoreValue)
|
||||
.doOnCompleted(() -> {
|
||||
newStoreValueEvent.onNext(newStoreValue);
|
||||
STORABLE_LC_GROUP.i("Value of '%s' changed from '%s' to '%s'", key, oldStoreValue, newStoreValue);
|
||||
if (checkForEqualityBeforeSet) {
|
||||
STORABLE_LC_GROUP.i("Value of '%s' changed from '%s' to '%s'", key, oldStoreValue, newStoreValue);
|
||||
} else {
|
||||
STORABLE_LC_GROUP.i("Value of '%s' force changed to '%s'", key, newStoreValue);
|
||||
}
|
||||
})
|
||||
.toObservable();
|
||||
}))
|
||||
|
|
@ -255,23 +257,48 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
STORABLE_LC_GROUP.w(throwable, "Exception while trying to store value of '%s' from store %s by %s",
|
||||
key, newValue, store, converter);
|
||||
}
|
||||
});
|
||||
})
|
||||
.toCompletable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates observable which is async setting value to store.
|
||||
* It is not checking if stored value equals new value.
|
||||
* In result it will be faster to not get value from store and compare but it will emit item to {@link #observe()} subscribers.
|
||||
* NOTE: It could emit ONLY completed and errors events. It is not providing onNext event! //TODO: it's Completable :(
|
||||
*
|
||||
* @param newValue Value to set;
|
||||
* @return Observable of setting process.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> forceSet(@Nullable final TObject newValue) {
|
||||
return internalSet(newValue, false).toObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates observable which is async setting value to store.
|
||||
* It is checking if stored value equals new value.
|
||||
* In result it will take time to get value from store and compare
|
||||
* but it won't emit item to {@link #observe()} subscribers if stored value equals new value.
|
||||
* NOTE: It could emit ONLY completed and errors events. It is not providing onNext event! //TODO: it's Completable :(
|
||||
*
|
||||
* @param newValue Value to set;
|
||||
* @return Observable of setting process.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> set(@Nullable final TObject newValue) {
|
||||
return internalSet(newValue, true).toObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value synchronously. You should NOT use this method normally. Use {@link #set(Object)} asynchronously instead.
|
||||
*
|
||||
* @param newValue Value to set;
|
||||
* @throws Converter.ConversionException Throws if {@link Converter} threw exception during conversion;
|
||||
* @throws Migration.MigrationException Throws if {@link Migration} threw exception during migration.
|
||||
*/
|
||||
@Deprecated
|
||||
//deprecation: it should be used for debug only and in very rare cases.
|
||||
public void setSync(@Nullable final TObject newValue) throws Throwable {
|
||||
final ObservableResult<?> setResult = RxUtils.executeSync(set(newValue));
|
||||
if (setResult.getError() != null) {
|
||||
throw setResult.getError();
|
||||
}
|
||||
public void setSync(@Nullable final TObject newValue) {
|
||||
set(newValue).toBlocking().subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -300,21 +327,12 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
* Gets value synchronously. You should NOT use this method normally. Use {@link #get()} or {@link #observe()} asynchronously instead.
|
||||
*
|
||||
* @return Returns value;
|
||||
* @throws Converter.ConversionException Throws if {@link Converter} threw exception during conversion;
|
||||
* @throws Migration.MigrationException Throws if {@link Migration} threw exception during migration.
|
||||
*/
|
||||
@Deprecated
|
||||
//deprecation: it should be used for debug only and in very rare cases.
|
||||
@Nullable
|
||||
public TObject getSync() throws Throwable {
|
||||
final ObservableResult<TObject> getResult = RxUtils.executeSync(get());
|
||||
if (getResult.getError() != null) {
|
||||
throw getResult.getError();
|
||||
}
|
||||
if (getResult.getItems().size() != 1) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return getResult.getItems().get(0);
|
||||
public TObject getSync() {
|
||||
return get().toBlocking().first();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -376,23 +394,25 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
private TObject defaultValue;
|
||||
@Nullable
|
||||
private Scheduler storeScheduler;
|
||||
private long cacheTimeMillis;
|
||||
|
||||
protected BuilderCore(@NonNull final TKey key,
|
||||
@NonNull final Type objectType,
|
||||
@NonNull final Type storeObjectType,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter) {
|
||||
this(key, objectType, storeObjectType, store, converter, null, null, null, null);
|
||||
this(key, objectType, storeObjectType, store, converter, null, null, null, null, DEFAULT_CACHE_TIME_MILLIS);
|
||||
}
|
||||
|
||||
protected BuilderCore(@NonNull final BuilderCore<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
this(sourceBuilder.key, sourceBuilder.objectType, sourceBuilder.storeObjectType,
|
||||
sourceBuilder.store, sourceBuilder.converter, sourceBuilder.observeStrategy,
|
||||
sourceBuilder.migration, sourceBuilder.defaultValue, sourceBuilder.storeScheduler);
|
||||
sourceBuilder.migration, sourceBuilder.defaultValue, sourceBuilder.storeScheduler, sourceBuilder.cacheTimeMillis);
|
||||
}
|
||||
|
||||
@SuppressWarnings("CPD-START")
|
||||
@SuppressWarnings({"PMD.ExcessiveParameterList", "CPD-START"})
|
||||
//CPD: it is same code as constructor of Storable
|
||||
//ExcessiveParameterList: that's why we are using builder to create it
|
||||
private BuilderCore(@NonNull final TKey key,
|
||||
@NonNull final Type objectType,
|
||||
@NonNull final Type storeObjectType,
|
||||
|
|
@ -401,7 +421,8 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
@Nullable final ObserveStrategy observeStrategy,
|
||||
@Nullable final Migration<TKey> migration,
|
||||
@Nullable final TObject defaultValue,
|
||||
@Nullable final Scheduler storeScheduler) {
|
||||
@Nullable final Scheduler storeScheduler,
|
||||
final long cacheTimeMillis) {
|
||||
this.key = key;
|
||||
this.objectType = objectType;
|
||||
this.storeObjectType = storeObjectType;
|
||||
|
|
@ -411,6 +432,7 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
this.migration = migration;
|
||||
this.defaultValue = defaultValue;
|
||||
this.storeScheduler = storeScheduler;
|
||||
this.cacheTimeMillis = cacheTimeMillis;
|
||||
}
|
||||
|
||||
@SuppressWarnings("CPD-END")
|
||||
|
|
@ -426,6 +448,10 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
this.migration = migration;
|
||||
}
|
||||
|
||||
protected void setCacheTimeInternal(final long cacheTime, @NonNull final TimeUnit timeUnit) {
|
||||
this.cacheTimeMillis = timeUnit.toMillis(cacheTime);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected TObject getDefaultValue() {
|
||||
return defaultValue;
|
||||
|
|
@ -478,6 +504,20 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cache time for while value that cached by {@link #setObserveStrategy(ObserveStrategy)} will be in memory after everyone unsubscribe.
|
||||
* It is important for example for cases when user switches between screens and hide/open app very fast.
|
||||
*
|
||||
* @param cacheTime Cache time value;
|
||||
* @param timeUnit Cache time units.
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder<TKey, TObject, TStoreObject> setCacheTime(final long cacheTime, @NonNull final TimeUnit timeUnit) {
|
||||
setCacheTimeInternal(cacheTime, timeUnit);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specific {@link Migration} to migrate values from specific version to latest version.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -20,11 +20,15 @@
|
|||
package ru.touchin.roboswag.core.observables.storable.builders;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Migration;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Scheduler;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 15/05/2016.
|
||||
|
|
@ -42,6 +46,45 @@ public class NonNullStorableBuilder<TKey, TObject, TStoreObject> extends Storabl
|
|||
setDefaultValueInternal(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specific {@link Scheduler} to store/load/convert values on it.
|
||||
*
|
||||
* @param storeScheduler Scheduler;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setStoreScheduler(@Nullable final Scheduler storeScheduler) {
|
||||
setStoreSchedulerInternal(storeScheduler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specific {@link Storable.ObserveStrategy} to cache value in memory in specific way.
|
||||
*
|
||||
* @param observeStrategy ObserveStrategy;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setObserveStrategy(@Nullable final Storable.ObserveStrategy observeStrategy) {
|
||||
setObserveStrategyInternal(observeStrategy);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cache time for while value that cached by {@link #setObserveStrategy(Storable.ObserveStrategy)}
|
||||
* will be in memory after everyone unsubscribe.
|
||||
* It is important for example for cases when user switches between screens and hide/open app very fast.
|
||||
*
|
||||
* @param cacheTime Cache time value;
|
||||
* @param timeUnit Cache time units.
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setCacheTime(final long cacheTime, @NonNull final TimeUnit timeUnit) {
|
||||
setCacheTimeInternal(cacheTime, timeUnit);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specific {@link Migration} to migrate values from specific version to latest version.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class NonNullStorable<TKey, TObject, TStoreObject> extends Storable<TKey,
|
|||
|
||||
@NonNull
|
||||
@Override
|
||||
public TObject getSync() throws Throwable {
|
||||
public TObject getSync() {
|
||||
final TObject result = super.getSync();
|
||||
if (result == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
|
|
|
|||
Loading…
Reference in New Issue