I created an Observable which emits the ViewPager's positionOffset.
public Observable<Float> observable() {
return Observable.create(new Observable.OnSubscribe<Float>() {
#Override
public void call(final Subscriber<? super Float> subscriber) {
if (viewPager == null) {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(new IllegalStateException("viewPager is null"));
}
return;
}
final ViewPager.OnPageChangeListener listener = new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(final int position,
final float positionOffset,
final int positionOffsetPixels) {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(positionOffset);
}
}
#Override
public void onPageSelected(final int position) {
}
#Override
public void onPageScrollStateChanged(final int state) {
}
};
viewPager.addOnPageChangeListener(listener);
subscriber.add(Subscriptions.create(() -> viewPager.removeOnPageChangeListener(listener)));
}
});
}
It works so far.
But with every subscription it creates a new ViewPager.OnPageChangeListener and adds it to the ViewPager.
Is there a way that all subscriptions share the same Observable that the ViewPager has only one Listener?
You can use Observable.share(), that's good option if you have multiple subscribers and dont mind if late subscribers can loose some notifications.
If you want that all subscribers see same notifications, use Observable.publish() - it will return ConnectableObservable, which starts emitting items once its connect() method is called. So you can do
connectable = originalObservable.publish();
connectable.subscribe(observer1);
connectable.subscribe(observer2);
connectable.connect();
You can use Connectable observables.
For example the code below simulates infinite (never completing) stream of events:
Observable<Float> o = Observable.concat(Observable.just(1f,2f,3f), Observable.never());
You can create a connectable observable using replay with refCount that will cache the last value:
Observable<Float> shared = o.replay(1).refCount();
A first subscriber will make the observable "hot" and will force it to produce items:
shared.subscribe(new Action1<Float>() {
#Override
public void call(Float t) {
System.out.println("o1:" + t);
}
});
Thread.sleep(1000);
A second observable may connect to it after a while, and will start from the latest item emitted:
shared.subscribe(new Action1<Float>() {
#Override
public void call(Float t) {
System.out.println("o2:" + t);
}
});
Thread.sleep(1000);
Output:
o1:1.0
o1:2.0
o1:3.0
o2:3.0
Related
I want to set a count down timer inside each event in recyclerview.
I tried it inside onBindViewHolder. It is working but at the same time it is also affecting the UI.
For example, click events are not working.
Is the any best approach to resolve it?
You could use Observable.interval from RxJava
Here's a util function for countdowntimer
public static Observable<Long> countdownTimer(long countdownValue, Observer<Long> observer) {
if (observer == null) return Observable.just((long) 0);
//.interval is a cold observable
// it will emit separately for every observer
Observable<Long> timerObservale = Observable
.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.takeWhile(new Predicate<Long>() {
#Override
public boolean test(Long aLong) throws Exception {
return aLong <= countdownValue;
}
})
.doOnError(Throwable::printStackTrace)
.observeOn(AndroidSchedulers.mainThread());
timerObservale.subscribe(observer);
return timerObservale;
}
Here's how you would use it
Utils.countdownTimer(60, new Observer<Long>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Long aLong) {
//do somethng
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
});
The utils function emits a long every second for a given period of time
I am new to ReactiveX and I have a case where I want my observable to emit data to a late subscriber(whenever the observer subscribes, observable should emit the same data that it emitted previously). I made this Observable class that provide ReplaySubject's same instance to all observers (it is singleton class).
public class AccountsObservable {
private static ConnectableObservable<String> hotObservable;
private static AccountsObservable accountsObservable;
public static AccountsObservable getObject() {
if (accountsObservable == null) {
accountsObservable = new AccountsObservable();
}
return accountsObservable;
}
public ConnectableObservable<String> getObservable() {
if (hotObservable == null) {
Observable<String> observable = ReplaySubject.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("XYZ");
emitter.onComplete();
}
});
hotObservable = observable.replay();//publish
}
return hotObservable;
}
}
Similarly, this is the observer class that creates new observer instance.
public class AccountsObserver {
AccountsFetchListener listener;
public AccountsObserver(AccountsFetchListener listener) {
this.listener = listener;
}
public Observer<String> getObserver() {
return new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String accounts) {
listener.onSuccess(accounts);
}
#Override
public void onError(Throwable e) {
listener.onFailure();
}
#Override
public void onComplete() {
}
};
}
public interface AccountsFetchListener {
void onSuccess(String accounts);
void onFailure();
}
}
Here is the function where I test these observables
private void testObs() {
ConnectableObservable<String> observable = AccountsObservable.getObject().getObservable();
Observer<String> observer = new AccountsObserver(new AccountsObserver.AccountsFetchListener() {
#Override
public void onSuccess(String accounts) {
Log.e("DATA -> ", accounts);
}
#Override
public void onFailure() {
}
}).getObserver();
observable.subscribe(observer);
observable.connect();
}
I called this function "testObs()" 5 times but it emitted data only 2 times. The problem seems to be in AccountsObservable class where I provide ReplaySUbject's instance. Thanks
Your code runs fine as it is, your logs are being suppressed in logcat as per this:
We declared an application as too chatty once it logs more than 5 lines a second. Please file a bug against the application's owner that is producing this developer-verbose-debug-level class logging spam. The logs are 256KB, that means the application is creating a DOS attack and shortening the logs timepan to 6 seconds(!) making it useless for all others.
You can avoid this behaviour by whitelisting your app for logcat:
adb logcat -P '<pid or uid of your app>'
I have 2 streams, the first stream is a stream which takes data from database and call onCompleted() after finish taking data. The second stream is a stream that takes live data from server and never call onCompleted(). What I want to do is to create an operator that can do an action if the first stream(upstream) is an empty stream. Here is the sample:
getItemFromDatabase()
.lift(new DoIfEmptyOperator<Item>(new Action0() {
#Override
public void call() {
//Database is empty
System.out.println("Yeay successfully do an action");
}
}))
.concatWith(getItemFromServer()) // -----> intentionally never complete
.subscribe(new Subscriber<StoryItem>() {
#Override
public void onCompleted() {
//dosomething...
}
#Override
public void onError(Throwable e) {
//dosomething...
}
#Override
public void onNext(StoryItem storyItem) {
//dosomething
}
}));
Here is the code of DoIfEmptyOperator:
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
public class DoIfEmptyOperator<T> implements Observable.Operator<T,T>{
private Action0 action;
private boolean isEmpty = true;
public DoIfEmptyOperator(Action0 action) {
this.action = action;
}
#Override
public Subscriber<? super T> call(final Subscriber<? super T> childSubscriber) {
Subscriber<T> parentSubscriber = new Subscriber<T>() {
#Override
public void onCompleted() {
if(isEmpty) {
action.call();
}
childSubscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
childSubscriber.onError(e);
}
#Override
public void onNext(T t) {
isEmpty = false;
childSubscriber.onNext(t);
}
};
childSubscriber.add(parentSubscriber);
return parentSubscriber;
}
}
However the action is never executed because the parentSubscriber onCompleted() is not firing, because the downstream never completed. If I remove
.concatWith(getItemFromServer())
then the action is executed. Any clue about how to solve the problem? I have dived to the source code of Observable.switchIfEmpty() but still have no clue about how it works.
I would advise against creating an operator.
This could be easily done with existing operators like this:
getItemFromDatabase()
.toList()
.flatMap(list -> {
if (list.isEmpty()) {
// side effect here
}
return getItemFromServer();
});
Have you thought about switchIfEmpty()? As an example of the usage of this operator - I have created some code on GitHub at the following link:
https://github.com/rs146/rxjava-simple/blob/master/src/test/java/SwitchIfEmpty.java
switchIfEmpty() is called when no items are emitted.
However, if you want to get items from the api or the db, then you can do something like the following:
Observable.concat(getFromDatabase(), getFromApi()).first();
As long as both getFromDatabase() and getFromApi() return the same Observable Type. This is a common Rx idiom in Android apps. It basically states that if an item's is not emitted from the database, then go fetch the result from the API instead.
I'm creating an Observable which emits Integers when subscribed to. My implementation right now is set up so the act of subscribing to it triggers the generation from the start, as follows:
private Observable createObservable() {
return Observable.create (
new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> sub) {
for (int i = 1; i < MAX_PROGRESS + 1; i++) {
sub.onNext(i);
SystemClock.sleep(1000);
}
sub.onCompleted();
}
}
);
}
My understanding is this is a cold Observable. I want the sequence to be generated irrespective of any subscribers, and when a subscriber does subscribe, want them to receive the values which happen to be current at the time of the subsciption. IOW, turn this into a hot Observable. I'd rather not subclass Observable because that ties it into a concrete Integer, whereas in practice the actual type will vary.
Check out rx.subjects.BehaviorSubject<T>. If you aren't familiar with rx.subjects.Subjects the most general way that I can think to describe them is that they break the continuity of subscriptions between point A and B. How is does it is by being both an Observer<T>; Can accept onNext()s from multiple sources (WARNING: external thread safety required). On the other side a subject is also an Observable<T> so multiple Observer<T>s can subscribe and onNext()s coming in will be multicast out to each downstream Observer<T>.
If your code looked like
Observable<T> src = ...;
Subscriber<T> dst;
src.subscribe(dst);
The way to use the BehaviorSubject is
Observable<T> src = ...;
BehaviorSubject<T> subject = BehaviorSubject.create(defaultValue);
src.subscribe(subject);
subscribe immediately to the source and the subject will take is as fast as it is dished out. The BehaviorSubject only keeps the more recent value and drops the defaultValue & all previous values.
// safe to do multiple times.
Subscriber<T> dst;
subject.subscribe(dst);
On subscribe dst receives the most recent value from the src (or the defaultValue) immediately upon subscribing and then all subsequent values until dst unsubscribes.
WARNING: Subjects have a tendency to overused so be sure you need one.
In your example you using "Observable.create" each time calling function. "Hot" subscription save some instance from Observable. Also you need use some Rx methods (cache(), retry()) In code it's look like:
public Observable<Bitmap> mObservable;
Subscriber<Bitmap> mSubscriber;
Subscription mSubscription;
Bitmap loadedBitmap;
....
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (savedInstanceState != null) {
loadedBitmap = savedInstanceState.getParcelable("LoadedBitmap");
imageView.setImageBitmap(loadedBitmap);
}
else {
runNewObservable();
}
runSubscribe();
return mainView;
}
....
private void runNewObservable () {
mObservable =
Observable.create(new Observable.OnSubscribe<Bitmap>() {
#Override
public void call(Subscriber<? super Bitmap> subscriber) {
subscriber.onNext(new LoadingImage().loadImageFrom(imageURL));
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.cache();
}
private void runNewSubscribe () {
mSubscriber = new Subscriber<Bitmap>() {
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
#Override
public void onNext(Bitmap bitmap) {
loadedBitmap = bitmap;
imageView.setImageBitmap(bitmap);
}
};
}
#Override
public void onResume() {
super.onResume();
mSubscription = mObservable.subscribe(mSubscriber);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("LoadedBitmap", loadedBitmap);
}
#Override
public void onStop() {
super.onStop();
mSubscription.unsubscribe();
}
....
As you can see. I use just mSubscription.subscribe() and mSubscription.unsubscribe() and create Observable only when saveInstanceState is null.
I'm looking a way to define order(?) of observers.
#GET("/get_user_msgs")
Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params);
For example I gave a Observable from my Rest API created by Retrofit.
In my ListView I'm observing this Observable.
api.getPrivateMessages(params).subscribe(new Observer());
I also have an API wrapper for my Espresso tests and I'm subscribing to same Observable there. This way observer in API wrapper is called first and only then observer in ListView
is called.
public class IdlingWrapper implements Api, IdlingResource {
....
public IdlingWrapper(Api realApi) {
this.realApi = realApi;
}
...
public Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params); {
counter.incrementAndGet();
return wrapObservable(realApi.getPrivateMessages(params));
}
protected <T> Observable<T> wrapObservable(final Observable<PrivateMessagesResponse> observable) {
//what to do here?
}
}
Is there a way to force some observer to be notified after all others are done? Or something similar in that matter?
Something like
Observable observable = getObservable();
observable.subscribeAsLast(new LastObserver());
observable.subscribe(new ObserverA());
observable.subscribe(new ObserverB());
And so that ObserverA would be notified first, then ObserverB and only then LastObserver.
Or any other approach where I could find out when all registered observers were notified and completed.
I'm not exactly sure what you are trying to do in IdlingWrapper, but I think the current implementation is very fragile.
I think the most important thing that needs to happen is to guarantee the observable can only be called once.
Here is a quick implementation to demonstrate that as well as my implementation of wrapObservable.
public class Test {
private static int counter = 0;
private static final List<Observable<?>> list = Collections.synchronizedList(new ArrayList<>());
protected static <T> Observable<T> wrapObservable(final Observable<T> original) {
// run atleast once???
synchronized (list) {
list.add(original);
}
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
synchronized (list) {
counter++;
if (!list.contains(original)) {
subscriber.onError(new Exception("You can only subscribe once!"));
return;
}
list.remove(original);
}
// Sleep to make it easier to see things happening...
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
subscriber.onCompleted();
}
}).flatMap(new Func1<Void, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Void o) {
return original;
}
}).finallyDo(new Action0() {
#Override
public void call() {
synchronized (list) {
counter--;
if (list.size() == 0 && counter == 0) {
System.err.println("finally");
}
}
}
});
}
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10; i++) {
// running in io thread for simulating async call.
Observable<String> test = wrapObservable(Observable.from("TEST!!!!!!")).subscribeOn(Schedulers.io());
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
// example of calling the same observable twice.
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
}
Thread.sleep(10000);
}
}
It seems, that this worked just fine.
protected <T> Observable<T> wrapObservable(final Observable<T> original) {
return Observable.create(new Observable.OnSubscribeFunc<T>() {
#Override
public Subscription onSubscribe(final Observer<? super T> t1) {
original.subscribe(new Observer<T>() {
#Override
public void onCompleted() {
t1.onCompleted();
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onError(Throwable e) {
t1.onError(e);
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onNext(T args) {
t1.onNext(args);
}
});
return Subscriptions.empty();
}
});
}
If you want to just use built in RxJava methods to order your observers, you can use flatMap and range to turn each item into multiple items each with a priority and then filter on priority. Observers are ordered based on how they filter.
Here's a trivial example:
Observable<Pair<Integer, Object>> shared = RxView.clicks(findViewById(R.id.textView))
.flatMap(c -> Observable.range(0, 2).map(i -> Pair.create(i, c)))
.share();
shared.filter(p -> p.first == 1)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "first subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "first subscribed onNext"));
shared.filter(p -> p.first == 0)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "second subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "second subscribed onNext"));
If you are doing this all over the place