0

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?

Anton Makov
  • 825
  • 2
  • 9
  • 25
  • This eventbus implementation is a bit complex. You could have just had a single subject and let it publish. Next thing, your message object should contain two values, i.e int or String event, T value. Then in your subscriber you can check the event in a switch case and cast your value accordingly – Debanjan Feb 19 '18 at 02:36
  • Can you give an example of how to implement it, I also need to send several arguments, I thought of sending a bundle but I doubt it's the best solution to send data between the fragment and the activity – Anton Makov Feb 19 '18 at 16:04
  • The best way for interaction is now through databinding, viewmodel and live data. These are immensely powerful https://developer.android.com/topic/libraries/architecture/viewmodel.html#sharing However I will provide an answer about rxBus – Debanjan Feb 19 '18 at 18:19

1 Answers1

0

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.

Debanjan
  • 2,817
  • 2
  • 24
  • 43
  • How can I send multiple parameters with your solution? – Anton Makov Feb 19 '18 at 21:53
  • ActionEvent takes an action, and the event can be any object. You can make a class with multiple variables, and pass it's object in value or pass a Map object. In the subscriber or observer, you can check the event by it's action, preferably in a switch case, and take the corresponding value. – Debanjan Feb 20 '18 at 02:50
  • If I will create a wrapper class that will contain the action (to know what action to make) and a bundle or a map if there are any arguments I need to apply to take to consider, would it be like your solution or there are other advantages to your solution?, I updated my main post with another question about EventBus – Anton Makov Feb 20 '18 at 11:55
  • That'll do. But you are creating subjects for each action, you can just use one, and your wrapper class should be generic(just the same thing I did here) so that you can put it in a switch case. This is what I think, now, if you find any better approach I will be glad to follow, since my implementation still has some limitations. – Debanjan Feb 20 '18 at 12:59