I am currently just using EventBus to transfer data from FirebaseMessagingService onMessageReceived to MainActivity , but this is getting tricky as complexity goes on and what if i get multiple notifications ? on the other hand,
Data transfer is costing 1 extra class and 2 boiler plate function due to EventBus.
So Problem is how to transfer data from FirebaseMessagingService to Activity using Rxjava , and is there any way to convert this whole service to some observables ?
You would still need to Service to receive the notifcations. However, you could use a PublishSubject to publish items like this:
class NotificationsManager {
private static PublishSubject<Notification> notificationPublisher;
public PublishSubject<Notification> getPublisher() {
if (notificationPublisher == null) {
notificationPublisher = PublishSubject.create();
}
return notificationPublisher;
}
public Observable<Notification> getNotificationObservable() {
return getPublisher().asObservable();
}
}
class FirebaseMessagingService {
private PublishSubject<Notification> notificationPublisher;
public void create() {
notificationPublisher = NotificationsManager.getPublisher()
}
public void dataReceived(Notification notification) {
notificationPublisher.onNext(notification)
}
}
class MyActivity {
private Observable<Notification> notificationObservable;
public void onCreate(Bundle bundle) {
notificationObservable = NotificationsManager.getNotificationObservable()
notificationObservable.subscribe(...)
}
}
Edit: expanded the example. Please note this is not the best way to do it, just an example
yes, you can convert Service for using Observables by using PublishSubject . Just return it as observable by
subject.asObservable() and pass it new event from onEvent() method by
subject.onNext().
Use Service binding to bind your Service to Activity, and , whithing binding interface, return references to your subjects as observables.
PublishSubject<String> eventPipe = PublishSubject.create();
Observable<String> pipe = eventPipe.observeOn(Schedulers.computation()).asObservable();
// susbcribe to that source
Subscription s = pipe.subscribe(value -> Log.i(LOG_TAG, "Value received: " + value));
// give next value to source (use it from onEvent())
eventPipe.onNext("123");
// stop receiving events (when you disconnect from Service)
if (s != null && !s.isUnsubscribed()){
s.unsubscribe();
s = null;
}
// we're disconnected, nothing will be printed out
eventPipe.onNext("321");
Related
I'm currently trying to move an application over to RxJava 2 to replace our broadcast receiver methods of passing events around. The goal here was to used Rx with a pubSub pattern in mind.
(Just a heads up, the following class which acts as an event bus was taken from an article I found online and I do believe I don't understand the working to 100% which I is probably why I can't find the fix of the problem).
My problem: I have a library that gives emits certain events (in this case a Bluetooth library that handles connection, pairing, data processing, etc...). The activities interested in these events can subscribe to certain or all of them to get notified when the library has something. I guess it is important to say that I AM getting responses from my library (so data), but I cannot change the UI (I can add words to a textview but that's about it) when my activity receives the response. I do understand that the threads that onNext() will run on is where the subscribe was called for, and as of right now, the subscribe on is NOT explicitly called by my activity ( I'll paste in the code below).
Snippet of activity registration to events:
EventBus.subscribe(EventBus.Events.CONNECTED, this, new Consumer<Object>()
{
#Override
public void accept(Object device)
{
if(device instanceof DeviceStructure)
{
if(uuid.equals(((DeviceStructure) device).getUUID()))
{
status_value.append("device connected " + ((DeviceStructure) device).getUUID());
}
else if(name.equals(((DeviceStructure) device).getName()))
{
status_value.append( "device connected " + name + " " + address);
}
}
}
}
);
EventBus.subscribe(EventBus.Events.PAIRED, this, new Consumer<Object>()
{
#Override
public void accept(Object device)
{
if(device instanceof DeviceStructure)
{
if(((DeviceStructure) device).getPairedStatus())
{
uuid = ((DeviceStructure) device).getUUID();
status_value.append("device paired " + uuid);
pair.setEnabled(false);
sync.setEnabled(true);
unpair.setEnabled(true);
}
}
}
});
I am able to modified the status_value values and display some text. However, I am unable to do pair.setEnabled(false) ( The part where the error gets thrown)
The event bus class:
public final class EventBus {
public enum Events
{
CONNECTED,
PAIRED
}
private static Map<Events, PublishSubject<Object>> sSubjectMap = new HashMap<>();
private static Map<Object, CompositeDisposable> sSubscriptionsMap = new HashMap<>();
private EventBus() {
// hidden constructor
}
/**
* Get the subject or create it if it's not already in memory.
*/
#NonNull
private static PublishSubject<Object> getSubject(Events event) {
PublishSubject<Object> subject = sSubjectMap.get(event);
if (subject == null) {
subject = PublishSubject.create();
subject.subscribeOn(AndroidSchedulers.mainThread());
sSubjectMap.put(event, subject);
}
return subject;
}
/**
* Get the CompositeDisposable or create it if it's not already in memory.
*/
#NonNull
private static CompositeDisposable getCompositeDisposable(#NonNull Object subscriber) {
CompositeDisposable compositeDisposable = sSubscriptionsMap.get(subscriber);
if (compositeDisposable == null) {
compositeDisposable = new CompositeDisposable();
sSubscriptionsMap.put(subscriber, compositeDisposable);
}
return compositeDisposable;
}
/**
* Subscribe to the specified subject and listen for updates on that subject. Pass in an object to associate
* your registration with, so that you can unsubscribe later.
* <br/><br/>
*/
public static void subscribe(Events subject, #NonNull Object subscriber, #NonNull Consumer<Object> action) {
Disposable disposable = getSubject(subject).subscribe(action);
getCompositeDisposable(subscriber).add(disposable);
}
/**
* Unregisters this object from the bus, removing all subscriptions.
* This should be called when the object is going to go out of memory.
*/
public static void unregister(#NonNull Object lifecycle) {
//We have to remove the composition from the map, because once you dispose it can't be used anymore
CompositeDisposable compositeDisposable = sSubscriptionsMap.remove(lifecycle);
if (compositeDisposable != null) {
compositeDisposable.dispose();
}
}
/**
* Publish an object to the specified subject for all subscribers of that subject.
*/
public static void publish(Events subject, #NonNull Object message) {
getSubject(subject).onNext(message);
}
}
I have tried the following solutions proposed online to indicate that I would like the subscriber to run on the main thread ( which should be the UI thread, unless I got that wrong).
subject.observeOn(AndroidSchedulers.mainThread());
subject.delay(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread());
subject.subscribeOn(AndroidSchedulers.mainThread());
subject.subscribeOn(AndroidSchedulers.from(Looper.getMainLooper()));
I have also found this link : https://github.com/ReactiveX/RxAndroid/issues/371 but the solution provided still doesn't seem to like what my activity is doing.
I guess it is also worth noting that when I enter the accept method in PAIRED action, the thread isn't the UI thread at that point.
Edit:
I'll leave this on just for other people like me. I had to add the observeOn to my disposable object and not in my getSubject() method which is where I added it from.
if (subject == null) {
subject = PublishSubject.create();
subject.subscribeOn(AndroidSchedulers.mainThread());
The result of subscribeOn is ignored.
Technically what you must do is somehow interject an observeOn(AndroidSchedulers.mainThread()) before you receive your event from the relay to the subscriber.
A possibility is to define an Observable.Transformer that you can compose which will apply the observeOn.
#SuppressWarnings("unchecked")
<T> Observable.Transformer<T, T> observeOnMain() {
return tObservable -> tObservable.observeOn(AndroidSchedulers.mainThread());
}
and
.compose(observeOnMain())
.subscribe(subscriber);
I created an application using MVP pattern, I found this tutorial link and decided to implement it in my application in order for the fragments to communicate with their activities. I moved the implementation of the Eventbus to the correspond activity presenter and fragment presenter in order to still use the MVP pattern. Now I'm facing a new problem, one of my fragments need to change two things in the activity parameters (toolbar related and ImageView drawable). Can I somehow differentiate which callback is from in the accept function?
RxBus class
public final class RxBus {
private static SparseArray<PublishSubject<Object>> sSubjectMap = new SparseArray<>();
private static Map<Object, CompositeDisposable> sSubscriptionsMap = new HashMap<>();
public static final int CHANGE_APP_BAR_LAYOUT = 0;
public static final int CHANGE_POSTER_IMAGE = 1;
#IntDef({CHANGE_APP_BAR_LAYOUT, CHANGE_POSTER_IMAGE})
#interface Subject {
}
private RxBus() {
// hidden constructor
}
/**
* Get the subject or create it if it's not already in memory.
*/
#NonNull
private static PublishSubject<Object> getSubject(#Subject int subjectCode) {
PublishSubject<Object> subject = sSubjectMap.get(subjectCode);
if (subject == null) {
subject = PublishSubject.create();
subject.subscribeOn(AndroidSchedulers.mainThread());
sSubjectMap.put(subjectCode, subject);
}
return subject;
}
/**
* Get the CompositeDisposable or create it if it's not already in memory.
*/
#NonNull
private static CompositeDisposable getCompositeDisposable(#NonNull Object object) {
CompositeDisposable compositeDisposable = sSubscriptionsMap.get(object);
if (compositeDisposable == null) {
compositeDisposable = new CompositeDisposable();
sSubscriptionsMap.put(object, compositeDisposable);
}
return compositeDisposable;
}
/**
* Subscribe to the specified subject and listen for updates on that subject. Pass in an object to associate
* your registration with, so that you can unsubscribe later.
* <br/><br/>
* <b>Note:</b> Make sure to call {#link RxBus#unregister(Object)} to avoid memory leaks.
*/
public static void subscribe(#Subject int subject, #NonNull Object lifecycle, #NonNull Consumer<Object> action) {
Disposable disposable = getSubject(subject).subscribe(action);
getCompositeDisposable(lifecycle).add(disposable);
}
/**
* Unregisters this object from the bus, removing all subscriptions.
* This should be called when the object is going to go out of memory.
*/
public static void unSubscribe(#NonNull Object lifecycle) {
//We have to remove the composition from the map, because once you dispose it can't be used anymore
CompositeDisposable compositeDisposable = sSubscriptionsMap.remove(lifecycle);
if (compositeDisposable != null) {
compositeDisposable.dispose();
}
}
/**
* Publish an object to the specified subject for all subscribers of that subject.
*/
public static void publish(#Subject int subject, #NonNull Object message) {
getSubject(subject).onNext(message);
}
}
MainPresenter class
public class MainPresenter extends BasePresenter<MainView> implements Observer<ConfigurationResponse>,Consumer<Object>
{
...
#Override
public void accept(Object o) throws Exception {
//here is the problem how can I know if I should call to changeAppBar or change Image url?
}
ClientPresenter class
public class ClientPresenter extends BasePresenter<SeriesSpecsView>
{
...
//I'm calling to those function withing the fragment when the user click on the ui
public void setPosterUrl(String posterUrl)
{
RxBus.publish(RxBus.CHANGE_POSTER_IMAGE,posterUrl);
}
public void setAppBarLayoutParams(boolean collapse)
{
RxBus.publish(RxBus.CHANGE_APP_BAR_LAYOUT,collapse);
}
}
I found a two solutions for this problem:
1) to check the object by calling instanceof function, not very effective and if I will need to send the same type of information between the two events?
2) Add another evenbus but I don't think it's logical to have separate eventbus for every event you want to have callback to your activity.
Thanks for your help
UPDATE
I encountered another problem(or at least potentially problem). I added a SwipeRefreshLayout to wrap my content(which is the framelayout, each fragment that I will have will be displayed in this container). My main reason to do it was to implement a single interface between the activity and all the fragments. Let's say you don't have a network connection I will display a message to the user to swipe down in order to try to refresh the current fragment. So far I have done this by adding SwipeRefreshLayout to each of the fragments that I have. It's basically the same code and I thought to merge all the code in one place in the activity. I would love to use the EventBus but from what I understand I would need to subscribe all the fragments to the "event" onRefresh.
How can I send the event to the appropriate fragment?
I use RxBus to transmit global events. You can also use this your way.
class RxBus {
private val busSubject: Subject<ActionEvent<out Any>> =
PublishSubject.create()
fun register( onNext:
Consumer<ActionEvent<out Any>>):Disposable{
return busSubject
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onNext)
}
fun post(event: ActionEvent<out Any>) {
busSubject.onNext(event)
}
}
open class ActionEvent<T>(val action: ActionEnum
, val event: T) {
}
You can use String in place of ActionEnum, which is just an enum class
When you post something,
getRxBus()?.post(ActionEvent(ActionEnum.CHANGE_APP_BAR_LAYOUT,collapse))
When you want to subscribe,
val disposable = rxBus.subscribe(Consumer{...})
Remember to dispose the disposale on destroy.
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 have a Bound Service which responsible for downloading files and thus it knows the downloading status/progress. And the UI (Fragment or Activity) has to show/update download progress from the service.
Actually i think the common way is to use BroadcastReciever or a CallBack from Activity. But i heard somewhere about using RxJava (ReactiveX Programming) and mediator class (and Dagger to inject it into both service and activity) which is mentioned below.
So my question is how to handle RxJava with these bunch of stuff? Any Code Samples? Is there another efficient way than using intents?
Resource:
More efficient way of updating UI from Service than intents? [ see the first answer update ]
Required of RxJava/RxAndroid
1) OBSERVABLES
2) OBSERVERS
3) SUBSCRIBE
Plan where you need to place your 1, 2, 3,
OBSERVABLES go where the data is created, so In your case SERVICE
OBSERVERS go where the data needs to be consumed(or displayed), so that's your ACTIVITY
SUBSCRIBE goes anywhere where you have access to OBSERVABLE & OBSERVER, so lets use ACVITITY for that
Procedure and Code:
Firstly,
Prepare your OBSERVABLE in service like this
class MyService extends Service {
static PublishSubject<String> data = PublishSubject.create();
#Override
public void onStarCommand(Intent intent,int i, int j){
# DO THIS ANYWHER WHERE YOU GENERATE DATA
data.onNext("Hello");
}
public static Observable<String> getObservable(){
return data;
}
}
Secondly,
Prepare your OBSERVER(and OBSERVABLE) in Activity's onCreate
Observable<String> observable = MyService.getObservable();
Observer<String> observer = new Observer<String>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ",e);
}
#Override
public void onNext(String text) {
Log.d(TAG, "DATA reveived here: "+text);
}
};
Lastly
Link both OBSERVER and OBSERVABLE in Activity, else Observable will not respond,
use this again in onCreate
observable.subscribe(observer);
DONE, Now when even the data is triggered from Service using onNext(), the data arrives in Activity
The Binder subclass you use when something binds to your Service can expose a method that returns an Observable which emits progress data.
The caveat with this approach is that you have two resources which you need to release when the Activity instance is no longer valid: the service connection and the observable subscription.
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