diff --git a/build.gradle b/build.gradle
index a014e9a..4f12a5a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -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'
}
diff --git a/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java b/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java
index 04af630..fd7303d 100644
--- a/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java
+++ b/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java
@@ -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);
diff --git a/src/main/java/ru/touchin/roboswag/core/log/Lc.java b/src/main/java/ru/touchin/roboswag/core/log/Lc.java
index 8ac2c6c..08f554b 100644
--- a/src/main/java/ru/touchin/roboswag/core/log/Lc.java
+++ b/src/main/java/ru/touchin/roboswag/core/log/Lc.java
@@ -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() {
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java b/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java
new file mode 100644
index 0000000..721bb99
--- /dev/null
+++ b/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java
@@ -0,0 +1,212 @@
+/**
+ * Copyright 2014 Netflix, Inc.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 the value type
+ */
+@SuppressWarnings({"PMD.AvoidUsingVolatile", "PMD.CompareObjectsWithEquals"})
+//AvoidUsingVolatile,CompareObjectsWithEquals: from OnSubscribeRefCount code
+public final class OnSubscribeRefCountWithCacheTime implements OnSubscribe {
+
+ @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 onSubscribe(@NonNull final Subscriber super T> subscriber,
+ @NonNull final AtomicBoolean writeLocked) {
+ return new Action1() {
+ @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(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();
+ }
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/RxUtils.java b/src/main/java/ru/touchin/roboswag/core/observables/RxUtils.java
deleted file mode 100644
index 5da00f1..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/RxUtils.java
+++ /dev/null
@@ -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 Type of {@link Observable}'s items;
- * @return {@link ObservableResult} which contains all items and errors collected during execution.
- */
- @NonNull
- public static ObservableResult executeSync(@NonNull final Observable observable) {
- final ObservableResult 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() {
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java
index 7841f50..e6529c2 100644
--- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java
+++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java
@@ -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 {
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 {
public Storable(@NonNull final BuilderCore 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 store,
- @NonNull final Converter converter,
- @Nullable final ObserveStrategy observeStrategy,
- @Nullable final Migration 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 store,
+ @NonNull final Converter converter,
+ @Nullable final ObserveStrategy observeStrategy,
+ @Nullable final Migration 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 {
= 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 {
@NonNull
private Observable createStoreValueObservable(@NonNull final ObserveStrategy observeStrategy,
@Nullable final Migration migration,
- @Nullable final TObject defaultValue) {
+ @Nullable final TObject defaultValue,
+ final long cacheTimeMillis) {
final Observable result = (migration != null
? migration.migrateToLatestVersion(key).subscribeOn(scheduler)
: Completable.complete())
@@ -149,13 +154,14 @@ public class Storable {
.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 createValueObservable(@NonNull final Observable storeValueObservable,
- @NonNull final ObserveStrategy observeStrategy) {
+ @NonNull final ObserveStrategy observeStrategy,
+ final long cacheTimeMillis) {
final Observable result = storeValueObservable
.switchMap(storeObject -> Observable
.fromCallable(() -> converter.toObject(objectType, storeObjectType, storeObject))
@@ -169,7 +175,7 @@ public class Storable {
}
}));
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 {
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 {
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 {
* 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 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 {
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 store,
@NonNull final Converter 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 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 {
@Nullable final ObserveStrategy observeStrategy,
@Nullable final Migration 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 {
this.migration = migration;
this.defaultValue = defaultValue;
this.storeScheduler = storeScheduler;
+ this.cacheTimeMillis = cacheTimeMillis;
}
@SuppressWarnings("CPD-END")
@@ -426,6 +448,10 @@ public class Storable {
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 {
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 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.
*
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java
index 65c4716..ab5cfba 100644
--- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java
+++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java
@@ -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 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 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 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 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.
*
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java
index 95dc8ec..16840a9 100644
--- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java
+++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java
@@ -42,7 +42,7 @@ public class NonNullStorable extends Storable