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);
}
}
Related
I have a rather general question.
Assuming I have a RecyclerView in some kind of a MainActivity. The content of this RecyclerView is being updated in multiple places in other activities.
For example there could be the possibility to make new entries by starting a new Activity and saving it there.In that case I would intuitively start that activity with startActivityForResult() and receive the changes in the onActivityResult() method in the MainActivity.
But lets say deeper inside the application, there is the possibility to delete all entries. Now how do we notify the MainActivity about this change in the dataset? I thought about setting a flag of some kind and clearing it after the content has been updated in the MainActivity. But somehow using global variables does not really follow the principle of proper encapsulation, does it?
Sorry for this vague question, but I find it quite hard to properly handle information flow in Android in a elegant manner, so here we are.
How about a local broadcast? You can find the idea of broadcast in this document. You need local broadcast and it is preferred if you want to pass data within your app only.
Android apps can send or receive broadcast messages from the Android system and other Android apps, similar to the publish-subscribe design pattern. These broadcasts are sent when an event of interest occurs. For example, the Android system sends broadcasts when various system events occur, such as when the system boots up or the device starts charging. Apps can also send custom broadcasts, for example, to notify other apps of something that they might be interested in (for example, some new data has been downloaded).
You can use Handler to pass the Message in Activity and then You have to update RecyclerView. Like,
1) In Activity.
public static Handler mHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
if(msg.what == 1223){
//update RecyclerView
}
return false;
}
});
2) pass message When you want to update RecyclerView
Message msg = new Message();
msg.what = 1223;
Activity1.mHandler.sendMessage(msg);
You can use EventBus to handle it.
Define a class for your event
public static class MyEvent {
int event;
/* define your fields */
}
And prepare your subscriber in main activity
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMyEvent(MyEvent myEvent) {
switch(myEvent.event) {
/* Do what you need */
}
};
Now where you need to make change, call your subscriber like this:
MyEvent myEvent = new MyEvent();
myEvent.event = 1;
EventBus.getDefault().post(myEvent);
You can read more about EventBus in here
If you were using RxJava2, RxAndroid. Then you could try this.
Create a Bus:
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject = BehaviorSubject.create();
public static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
In your WaitingActivity where you want to receive data(where you want not to use onActivityResult in your case)
Disposable disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof DataObject) {
//((DataObject) o).getValue();
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
});
In your activity where you want to send data
RxBus.getSubject().onNext(dataObject);
startActivity(new Intent(CurrentActivity.class, WaitingActivity.class));
Finally don't forget to dispose your disposable to avoid memory leaks in your WaitingActivity
#Override
protected void onDestroy() {
super.onDestroy();
disposable.dispose();
}
Your data should be separate from view, in model. If some other activity changes data ideally recycler view must be updated from there. So no matter which activity does what, when you refresh data on load or resume of your recycler view you will always get correct results.
Background:
Nothing special, I'm using Java for Android.
Problem:
I want to allow my users to create multiple instances of an object and register a callback Interface into each instance (think of an error callback).
The object has several children/sub-children/etc... Each child can return this event.
I expect 1-10 instances in entire lifetime of app.
I'm trying to avoid using a singleton/static events listener.
Possible solutions (and reasons not to use them):
1) Register a BroadcastReceiver in each parent-object instance and allow each grand child notify the event on Intent level. This should allow the main object to notify my user about the event.
The problem is the multiple instances would require multiple BroadcastReceivers which I expect to be heavy or just less than optimal.
2) Register one BroadcastReceiver and find a way to let it decide which instance of the object should be notified of an event, and actually send it to it. The problem is that I'm not sure how to notify the objects themselves.
3) Let the callback interface pass as an argument from parent to each of the children/grandchilren/etc... But this would be messy.
4) Use something like EventBus library (which I understand would be the same thing as BroadcastReceiver, only Reflection based, thus slower).
5) Anything else?
I don't know if this is the best solution for you but I think it would work if I understand your requirements correctly.
public class Listener extends Observable implements Observer {
private List<Observer> clients = new ArrayList<>();
public void addClient(Observer client){
clients.add(client);
}
#Override
public void update(Observable o, Object arg) {
for(Observer client : clients){
client.update(o, arg); // Or whatever you need to do
}
}
public class DataSource extends Observable {
private Observer observer;
public DataSource(Observer o){
observer = o;
}
// Notify observer of changes at appropriate time
}
public class Consumer implements Observer {
public Consumer(){
Listener listener = ...;
listener.addClient(this);
}
#Override
public void update(Observable o, Object arg) {
// Handle appropriately
}
}
}
DataSource is your "sub-objects", Consumer is the end client of the events, and Listener is the class in the middle. I don't know why the clients can't directly register for events with the "sub-objects" but that is what you said! This is modeled as inner classes here for simplicity but I assume you would not do that.
New to RxJava and I have question about interface callbacks ( called from inner layer/module of code through interface variable) vs RxJava.
To make it more clear, quick example:
Standard callback interface implementation, interface, class A and B
interface CustomCallback {
void onCallbackCalled(String str);
}
class ClassA {
private ClassB classB;
public ClassA() {
classB = new ClassB(new CustomCallback() {
#Override
public void onCallbackCalled(String str) {
System.out.println("Callback called " + str);
}
});
}
}
class ClassB {
private CustomCallback customCallback;
public ClassB(CustomCallback callback) {
customCallback = callback;
}
private void somethingHappened() {
customCallback.onCallbackCalled("method somethingHappened");
}
}
When classB method "somethingHappened" is called, result is: "Callback called method somethingHappened".
Interface's method onCallbackCalled(String str) can be called from classB as many times as I want.
CLASS A ↓ ............................................ injection of interface through constructor
CLASS B................↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ onCallbackCalled(...) 0...n number
Now RxJava. 99% of cases which I find.
class ClassA {
private ClassB classB;
public ClassA() {
classB = new ClassB();
}
public void rxJavaMethod() {
DisposableObserver<String> observer = classB.getObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<String>() {
#Override
public void onNext(String s) {}
#Override
public void onError(Throwable e) {}
#Override
public void onComplete() {}
});
}
}
class ClassB {
private Observable<String> getObservable() {
return Observable.just(can be different from "just", for sake of example);
}
}
Scheme is:
CLASS A ↓........................ one call for getting Observable resource
CLASS B................↑ EDIT returns observable which emits 0...n values
So basically you call from top layer ( in this example) and you get response about state from inner layer.
QUESTIONS:
1) What in case when you have a model ( inner layer) which is changing dynamically ( but not any kind of AsyncTask etc.), and you want to notify top layer ( UI for example) that state has changed ( good example: game).
2) Is there any kind of "bridge" class in RxJava library ( I think about it as "subscribe to it, then you can pass arguments to it as many times as you want and information/observable will be emitted to subscribers).
3) Is there any sense and advanatage of trying to do that instead of standard interface callbacks ( in case like above, not " click button, get response once")
UPDATE, ANSWER BASED ON EXAMPLE ABOVE
As Bob Dalgleish mentioned, way of making such bridge is by using one of the class extending Subject<T> rxjava.
http://reactivex.io/documentation/subject.html
class ClassA {
private ClassB classB;
public ClassA() {
classB = new ClassB();
}
public void rxJavaMethod() {
DisposableObserver<String> observer = classB.getCallbackSubjectRx()
.subscribeWith(new DisposableObserver<String>() {
#Override
public void onNext(String s) {}
#Override
public void onError(Throwable e) {}
#Override
public void onComplete() {}
});
}
}
class ClassB {
private BehaviorSubject<String> mCallbackRx;
public ClassB() {
mCallbackRx = BehaviorSubject.create();
}
// method somethingHappened can be invoked whenever whe want and
// it will send given parameter to all subscribers
private void somethingHappened() {
mCallbackRx.onNext("method somethingHappened");
}
// multiple subscribers allowed
public BehaviorSubject<String> getCallbackSubjectRx() {
return mCallbackRx;
}
}
Downside might be, that if we want to use one "bridge" to handle multiple callback types ( interface have methods, we use only one method: "onNext()"), we might need to create wrapper class with callback parameters. Which isn't big problem in my opinion.
On the other hand, we get access to all of RxJava operators.
http://reactivex.io/documentation/operators.html
( Example above is for RxJava2, where Disposable is basically Subscription from RxJava1).
The first thing to note is that
CLASS B................↑ returns 0...n observables to observer
is not true. Class B returns an observable, on which it will occasionally emit 0..n values.
(the question is not clear). The inner observable, from class B, is changing state for whatever reason. The most common reason is that another process/task/thread is feeding it, and you want to display the resulting state in the UI.
A simple type of "bridging" class that I use all the time any of the several Subject<> classes. You can emit new values to them using .onNext() and the subscribers will get those values.
If callback interfaces were all standardized, then they would have some advantage, but they vary all over the place. You have to remember that you need some particular interface for this thing you are looking at and and a different one for the other thing. While UI events tend to be quite uniform these days, trying to mix UI events and network events and database events will still leave you feeling overwhelmed. Having a much smaller class of interfaces, mostly encapsulated inside of the rxJava generic classes, makes composing functionality much easier.
Edit: Improve example code.
There is a good article from Yammer Engineering on using Observable.create() (formerly Observable.fromEmitter(), formerly Observable.fromAsync(). The important points he makes are
Using Observable.create() handles the subscription step for you by registering a listener to the underlying interface. More importantly, it arranges to de-register the listener when the unsubscribe() occurs.
Out of the box, this code handles multiple subscribers, each of which receives its own observable stream of data.
As I mentioned above, the listener protocol is particular to the thing you register with. If that thing supports only a single listener, then you will likely want to introduce a Subject that subscribes to the thing under observation, and all your other observers subscribe to the subject.
End of edit.
My favorite example of composition of solutions is the distinctUntilChanged() operator. Because it is an operator that works on a generic observable, it encapsulates the stateful property of saving consecutive values for comparison and only emitting differing ones. I use it frequently for logging state changes. To achieve the same end using standard callback interfaces would require adding a different interface for saving prior values to every existing interface.
So, yes, most of the time it is worth using the rxJava approach of observables, simply for the sake of not having to remember which of the many call back protocols might be applicable in the current case.
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.
I am using a TextView inside of a Fragment.
I wish to update this TextView outside of the fragment (but also outside of an activity) from a callback class.
For example the user scrolls, the callback is called somewhere in my package, and I want the fragment view to be updated.
Can anybody explain how to do this? I did use a Local Broadcast Receiver but it wasn't fast enough in its updating.
Eventually looked at Otto but as we had Guava I implemented a singleton eventbus and used Guava publish/subscribe model to pass stuff around.
Otto however looks very similar.
Use Otto: http://square.github.io/otto/
public class UpdateEvent {
private String string;
public UpdateListEvent(String string) {
this.string = string;
}
public String getString() {
return string;
}
}
...
...
public void update() {
SingletonBus.INSTANCE.getBus().post(new UpdateListEvent(editText.getText().toString()));
}
...
public class FragmentA extends Fragment {
#Override
public void onResume() {
super.onResume();
SingletonBus.INSTANCE.getBus().register(this);
}
#Override
public void onPause() {
SingletonBus.INSTANCE.getBus().unregister(this);
super.onPause();
}
#Subscribe
public void onUpdateEvent(UpdateEvent e) {
//do something
}
}
public enum SingletonBus {
INSTANCE;
private Bus bus;
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public Bus getBus() {
return bus;
}
}
EventBus is a nice and elegant way for communication between modules in Android apps. In this way you should register your fragment as a event subscriber, and post a this specific event from other part of your code. Keep in mind that only UI thread can work with Views.
I don't exactly understand what you want to achieve and why BroadcastReceiver does not work for you, but you may either:
1) try using callbacks (if it is possible in your app design);
2) try using this or that event bus implementation;
Both would work pretty fast without much overhead, compared to broadcasting.
In case 2 you won't have to maintain callback dependencies/references.