Im exactly having this behavior
Subscriber OnComplete called twice
(which is is anticipated as per http://reactivex.io/documentation/subject.html)
But in my scenario : it goes something like this :
I have a AudioRecordingService which displays a notification, in which I have options for the user to save or delete the on going recording, which is working perfectly. But I'm trying to get into using RxAndroid, my notification's save button would trigger..
RxEventBus.getInstance().postEvent(new RxEvents(RxEventsEnum.AUDIO_STOP_AND_SAVE));
which triggers
bindUntilActivitySpecificEvent(RxEventBus.getInstance().forEventType(RxEvents.class),ActivityEvent.DESTROY).subscribeOn(
AndroidSchedulers.mainThread()).subscribe(new Action1<RxEvents>() {
#Override public void call(RxEvents rxEvents) {
onEvent(rxEvents);
}
});
and in my onEvent(rxEvent) based on the rxEvents object's data I appropriately save and store recording. The first time I try this, it works fine, but the subsequent times, the
#Override public void call(RxEvents rxEvents) {
onEvent(rxEvents);
}
is being called multiple times, like for example the second time I post an event, this callback is called twice, the third time thrice and so on... (which is actually what PublishSubject does). I don't want this behavior, I want Rx to be a able to post events and receive only the latest event that was posted and nothing else.
Here is my other relevant code
protected final <T> Observable<T> bindUntilActivitySpecificEvent(Observable<T> observable,
ActivityEvent event) {
return observable.compose(RxLifecycle.<T, ActivityEvent>bindUntilEvent(lifecycle(), event))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
and my run of the mill RxEventBus class :
public class RxEventBus {
private static final RxEventBus INSTANCE = new RxEventBus();
public static RxEventBus getInstance() {
return INSTANCE;
}
private RxEventBus() {
}
private final Subject<Object, Object> mBus = new SerializedSubject<>(PublishSubject.create());
public void postEvent(Object event) {
mBus.onNext(event);
}
public <T> Observable<T> forEventType(Class<T> eventType) {
return mBus.ofType(eventType).observeOn(AndroidSchedulers.mainThread());
}
}
What is the best approach using RxAndroid ? Please note that I am looking for RxAndroid solution only.
You are creating a new observable every time you trigger an event in
RxEventBus.getInstance().forEventType(RxEvents.class)
You need to cache the observables you create for each event type.
Related
I am trying to implement a feed app. I want to make the network call for new feed on Splash Activity which will run for a maximum of 3 seconds (irrespective of feed network call gets completed or not) and then move to my home screen where feed is visible to the user. I want the response of the call i made in Splash to be returned to my Home activity. I am using Retrofit with RxJava to make network calls. I have tried to use ConnectableObserver with replay operator to try this, but it is not working.
In SplashActivity's onCreate i call a method
MyClientNetworkUtil.initObservable();
The MyClientNetworkUtil class has following code
public class MyClientNetworkUtil {
private static MyClient client = ServiceGenerator.generateService(MyClient.class);
private static ConnectableObservable<List<DataModel>> x;
public static void initObservable() {
Observable<List<DataModel>> o = client.dataForUser("user");
x = o.subscribeOn(Schedulers.io()).publish();
x.connect();
}
public static ConnectableObservable<List<DataModel>> getObservable() {
return x.replay(1);
}
}
Then in my HomeActivity's onCreate function, i am trying to do :
ConnectableObservable<List<DataModel>> x = MyClientNetworkUtil.getObservable();
x.connect();
x.subscribe(response -> Log.d("Response", response.get(1).getName()), e -> Log.e("Response", e.toString()));
But none of the above log statements are executed.
Can you please help with what i am doing wrong here.
Thanks.
You have mistake of using ConnectableObservable.
connect method instructs it to begin begin emitting the items from
its underlying Observable to its Subscriber.
So you should subscribe first and then connect.
This code will work:
public static void main(String[] args) throws Exception {
initObservable();
getObservable().forEach(System.out::println);
x.connect();
Thread.sleep(1000);
}
private static ConnectableObservable<List<Integer>> x;
public static void initObservable() {
Observable<List<Integer>> o = Observable.range(1, 50).buffer(10);
x = o.subscribeOn(Schedulers.io()).publish();
// x.connect();
}
public static Observable<List<Integer>> getObservable() {
return x.replay(1).refCount();
}
But we have better way than connect. We can use refCount or autoConnect.
refCount
Returns an Observable that stays connected to this ConnectableObservable as long as there is at least one subscription to this ConnectableObservable.
autoConnect
Returns an Observable that automatically connects to this
ConnectableObservable when the first Subscriber subscribes.
I have a simple use case where:
Activity1 create a fragment1
fragment1 after creation notify to activity that it is created and update its activity1 views.
activity1 after getting notification update fragment1 views.
I am using rxandroid , sublibrary rxlifecycle components and android , but i am still in learning phase , there was not even rx-lifecycle tag on stackoverflow , so i am still struggling to understand the flow of this library..
Edit
I prefer not to use EventBus , it's just like everyone shouting at everyone to do something, so Rxjava Observable approach will be much useful
For posting information from fragment to activity, you should use an event bus for informing activity about fragment creation (replacement to the callbacks and the mess they created).
Sample code for event bus with RxJava is:
public class SampleEventsBus {
private static final String TAG = SampleEventsBus.class.getSimpleName();
private static final String TAG2 = SampleEventsBus.class.getCanonicalName();
public static final int ACTION_FRAGMENT_CREATED = 1;
public static final int ACTION_FRAGMENT_OTHER = 2;
private static SampleEventsBus mInstance;
public static SampleEventsBus getInstance() {
if (mInstance == null) {
mInstance = new SampleEventsBus();
}
return mInstance;
}
private SampleEventBus() {}
private PublishSubject<Integer> fragmentEventSubject = PublishSubject.create();
public Observable<Integer> getFragmentEventObservable() {
return fragmentEventSubject;
}
public void postFragmentAction(Integer actionId) {
fragmentEventSubject.onNext(actionId);
}
}
Then from your fragment you can call:
SampleEventsBus.getInstance().postFragmentAction(SampleEventsBus.ACTION_FRAGMENT_CREATED);
from onAttach() or onViewCreated() or any place you prefer.
Also, in activity you will need to put the following code to listet to your event bus:
SampleEventsBus .getInstance().getFragmentEventObservable().subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Integer actionId) {
if(actionId == SampleEventsBus.ACTION_FRAGMENT_CREATED) {
//do any required action
}
}
});
For the second part, i.e. to update the fragment from activity, I won't recommend using this method as it will lead to unnecessary complexity, Instead use the "original way" as:
Create a method in Fragment as updateView(Object obj)
In onNext(), get the desired fragment as SampleFragment fragment = (SampleFragment)getSupportFragmentManager().findFragmentByTag("TAG");
call fragment.updateView(obj);
Hope this helps.
Two points to consider:
Just because you use an EventBus does not mean that it needs to be
global. You can have multiple event buses if you want, and you can just
share a single one between two components (Activity and Fragment).
There are several examples in the RxJava documentation that show
how to implement event bus functionality using RxJava
By Using an event bus, you can simplify things greatly, by disassociating the whole process from the Android lifecycle.
I used to work with EventBus before, which was easy to use and easy to understand. This time, however, I would like to try out RxJava for event bus-like communication, however it is not very clear how to remove events from RxJava or, better to say, how it should be designed properly to have similar behavior as EventBus has when I call removeStickyEvent?
In RxJava I can use BehaviorSubject to reply last even when I subscribe to this observable, but what should I do when this event is handled? What if I do not want to have this event replayed again?
For instance, one fragment fires an event and then finishes. Another fragment listens to this event and handles it. Then, if this app fires that "another" activity again from different circumstances, then it will subscribe to the same BehaviorSubject again and will handle that stale event again, which is not what I would like to achieve.
I used this project as a reference https://github.com/marwinxxii/AndroidRxSamples/blob/master/app/src/main/java/com/github/marwinxxii/rxsamples/EventBusSampleActivity.java
As long as you do not plan to allow your events to be null, I think that can be achieved pretty easily.
Exactly as you said, you could use BehaviorSubject to propagate sticky events, and when you want to removeStickyEvent from the bus, you can just emit a null object (to "flush" the subject).
Something like this (from the top of my head - not tested, without generics, just a simple Object-event based example):
public class RxEventBus {
PublishSubject<Object> eventsSubject = PublishSubject.create();
BehaviorSubject<Object> stickyEventsSubject = BehaviorSubject.create();
public RxEventBus() {
}
public Observable<Object> asObservable() {
return eventsSubject;
}
public Observable<Object> asStickyObservable() {
return stickyEventsSubject.filter(new Func1<Object, Boolean>() {
#Override
public Boolean call(Object o) {
return o != null;
}
});
}
public void postEvent(#NonNull Object event) {
eventsSubject.onNext(event);
}
public void postStickyEvent(#NonNull Object stickyEvent) {
stickyEventsSubject.onNext(stickyEvent);
}
public void removeStickyEvent(){
stickyEventsSubject.onNext(null);
}
}
What I want to do is to create a simple in-memory cache just to try Observables out. However I got stuck because I don't understand how to create an observable. This is the code I have gotten so far:
public class MovieCache {
MovieWrapper movieWrapper;
public Observable<MovieWrapper> getMovies() {
//How to create and return an Observable<MovieWrapper> here?
}
public void setCache(MovieWrapper wrapper) {
movieWrapper = wrapper;
}
public void clearCache() {
movieWrapper = null;
}
}
In the getMovies() method I want to create an Observable and return my local field movieWrapper to the subscriber. How can I do this? I tried with using new Observable.just(movieWrapper) but it results in a null exception.
Take a look at this tutorial as it does exactly what you are looking for. Basically you use defer() to make sure you always get the latest version of your cached object:
public class MovieCache {
MovieWrapper movieWrapper;
public Observable<MovieWrapper> getMovies() {
return Observable.defer(new Func0<Observable<MovieWrapper>>() {
#Override
public Observable<MovieWrapper> call() {
return Observable.just(movieWrapper);
}
});
}
public void setCache(MovieWrapper wrapper) {
movieWrapper = wrapper;
}
public void clearCache() {
movieWrapper = null;
}
}
defer() makes sure that you will get the object upon subscription to the Observable not on creation.
Note however that, according to the author of the post:
The only downside to defer() is that it creates a new Observable each
time you get a subscriber. create() can use the same function for each
subscriber, so it's more efficient. As always, measure performance and
optimize if necessary.
As already said, accepted answer has downside
it creates a new Observable each time you get a subscriber
But it is not the only one.
Consumer won't receive any value if he calls getMovies().subscribe(...) before setCache(...) is called.
Consumer should resubscribe if he want to receive any updates (let's say setCache() can be called multiple times.
Of course all of them can be irrelevant in your scenario. I just want to show you another way (I'm sure there are many more).
You can use BehaviorSubject in order to eliminate all these disadvantages.
public class MovieCache {
private BehaviorSubject<MovieWrapper> mMovieCache = BehaviorSubject.create();
public void setCache(MovieWrapper wrapper) {
mMovieCache.onNext(wrapper);
}
public Observable<MovieWrapper> getMovieObservable() {
//use this if consumer want to receive all updates
return mMovieCache.asObservable();
}
public MovieWrapper getMovie() {
//use this if consumer want to get only current value
//and not interested in updates
return mMovieCache.getValue();
}
public void clearCache() {
//CAUTION consumer should be ready to receive null value
mMovieCache.onNext(null);
//another way is to call mMovieCache.onCompleted();
//in this case consumer should be ready to resubcribe
}
public static class MovieWrapper {}
}
Take a look at BehaviorSubject marble diagram.
I'm start learning RxJava and I like it so far. I have a fragment that communicate with an activity on button click (to replace the current fragment with a new fragment). Google recommends interface for fragments to communicate up to the activity but it's too verbose, I tried to use broadcast receiver which works generally but it had drawbacks.
Since I'm learning RxJava I wonder if it's a good option to communicate from fragments to activities (or fragment to fragment)?. If so, whats the best way to use RxJava for this type of communication?. Do I need to make event bus like this one and if that's the case should I make a single instance of the bus and use it globally (with subjects)?
Yes and it's pretty amazing after you learn how to do it. Consider the following singleton class:
public class UsernameModel {
private static UsernameModel instance;
private PublishSubject<String> subject = PublishSubject.create();
public static UsernameModel instanceOf() {
if (instance == null) {
instance = new UsernameModel();
}
return instance;
}
/**
* Pass a String down to event listeners.
*/
public void setString(String string) {
subject.onNext(string);
}
/**
* Subscribe to this Observable. On event, do something e.g. replace a fragment
*/
public Observable<String> getStringObservable() {
return subject;
}
}
In your Activity be ready to receive events (e.g. have it in the onCreate):
UsernameModel usernameModel = UsernameModel.instanceOf();
//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy
subscription = usernameModel.getStringObservable()
.subscribe(s -> {
// Do on new string event e.g. replace fragment here
}, throwable -> {
// Normally no error will happen here based on this example.
});
In you Fragment pass down the event when it occurs:
UsernameModel.instanceOf().setString("Nick");
Your activity then will do something.
Tip 1: Change the String with any object type you like.
Tip 2: It works also great if you have Dependency injection.
Update:
I wrote a more lengthy article
Currently I think my preferred approach to this question is this to:
1.) Instead of one global bus that handles everything throughout the app (and consequently gets quite unwieldy) use "local" buses for clearly defined purposes and only plug them in where you need them.
For example you might have:
One bus for sending data between your Activitys and your ApiService.
One bus for communicating between several Fragments in an Activity.
One bus that sends the currently selected app theme color to all Activitys so that they can tint all icons accordingly.
2.) Use Dagger (or maybe AndroidAnnotations if you prefer that) to make the wiring-everything-together a bit less painful (and to also avoid lots of static instances). This also makes it easier to, e. g. have a single component that deals only with storing and reading the login status in the SharedPreferences - this component could then also be wired directly to your ApiService to provide the session token for all requests.
3.) Feel free to use Subjects internally but "cast" them to Observable before handing them out to the public by calling return subject.asObservable(). This prevents other classes from pushing values into the Subject where they shouldn't be allowed to.
Define events
public class Trigger {
public Trigger() {
}
public static class Increment {
}
public static class Decrement {
}
public static class Reset {
}
}
Event controller
public class RxTrigger {
private PublishSubject<Object> mRxTrigger = PublishSubject.create();
public RxTrigger() {
// required
}
public void send(Object o) {
mRxTrigger.onNext(o);
}
public Observable<Object> toObservable() {
return mRxTrigger;
}
// check for available events
public boolean hasObservers() {
return mRxTrigger.hasObservers();
}
}
Application.class
public class App extends Application {
private RxTrigger rxTrigger;
public App getApp() {
return (App) getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
rxTrigger = new RxTrigger();
}
public RxTrigger reactiveTrigger() {
return rxTrigger;
}
}
Register event listener wherever required
MyApplication mApp = (App) getApplicationContext();
mApp
.reactiveTrigger() // singleton object of trigger
.toObservable()
.subscribeOn(Schedulers.io()) // push to io thread
.observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread
.subscribe(object -> { //receive events here
if (object instanceof Trigger.Increment) {
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1));
} else if (object instanceof Trigger.Decrement) {
if (Integer.parseInt(fabCounter.getText().toString()) != 0)
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1));
} else if (object instanceof Trigger.Reset) {
fabCounter.setText("0");
}
});
Send/Fire event
MyApplication mApp = (App) getApplicationContext();
//increment
mApp
.reactiveTrigger()
.send(new Trigger.Increment());
//decrement
mApp
.reactiveTrigger()
.send(new Trigger.Decrement());
Full implementation for above library with example -> RxTrigger