midnight
midnight

Reputation: 3412

Service - Fragment communication

An Activity contains a Fragment which in turn contains a child Fragment, which requests a Service. The app tries to implement dobjanschi rest architecture.

When the Service is done working it has to propagate operation result. I tried using a PendingIntent but it seems to only be caught by the activity, while I need the child fragment to get notified. Could you suggest anything? Binder? greenRobot Eventbus? RxJava (which I already have in the project)?

Thanks.

Upvotes: 2

Views: 4319

Answers (6)

wnc_21
wnc_21

Reputation: 1771

I would use event bus, which is based on rx. Make this as a sigletone and subscribe on particular class type.

public class RxBus {
    private static final RxBus sBus = new RxBus();
    private final Subject<Object, Object> mBus = new SerializedSubject<>(PublishSubject.create());

    private RxBus() {
    }

    public static RxBus getInstance() {
        return sBus;
    }

    public void send(Object o) {
        mBus.onNext(o);
    }

    public Observable<Object> observe() {
        return mBus;
    }

    @SuppressWarnings("unchecked")
    public <T> Observable<T> observe(Class<T> c) {
        return mBus.filter(o -> c.isAssignableFrom(o.getClass())).map(o -> (T) o);
    }
}

usage:

class Message { public String result};

send a message:

Message m = new Message();
m.result = "Hello world";
RxBus.getInstance().send(m);

subscribe on a particular class type:

RxBus.getInstance().observe(Message.class).subscribe(msg -> Log.e(TAG, "Message was caught : " + msg));

Upvotes: 0

miv
miv

Reputation: 349

RxJava

A simple way con be to use a Singleton to wrap a synchronized ´PublishSubject´

 * Singleton
 * 
 * to send an event use EventBusRx.getInstance().topic1.onNext("completed");
 */
public class EventBusRx {
    private static EventBusRx ourInstance = new EventBusRx();
    public static EventBusRx getInstance() {
        return ourInstance;
    }
    private EventBusRx() {}

    /**
     * Use of multiple topics can be usefull
     * SerializedSubject avoid concurrency issues
     */
    public final Subject<String, String> topic1 = new SerializedSubject<>(PublishSubject.create());
    public final Subject<Integer, Integer> topic2 = new SerializedSubject<>(PublishSubject.create());
}

And You can send events from service

EventBusRx.getInstance().topic1.onNext("completed");

and respond to event in fragments or whenever you want

public class MyFragment extends Fragment {

    // [...]

    Subscription subscription_topic1;

    @Override
    public void onResume() {
        super.onResume();

        subscription_topic1 = EventBusRx.getInstance().topic2
                .subscribeOn(AndroidSchedulers.mainThread()) // or on other sheduler
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                        // update ui
                    }
                });
    }

    @Override
    public void onPause() {
        // important to avoid memory leaks
        subscription_topic1.unsubscribe();
        super.onPause();
    }
}

do not forget to unsubcribe the Subscription

The idea is similar to Roger'one use a singleton but enforce ThreadSafety wrapping PublishSubject.

there is no need for Observable.switchOnNext(subject)

EventBus Libraries

greenRobot Eventbus and Otto are nice and has the same functionality, but the disadvantage is that they make the connection more smoky (expecialy EventBus) . If you already use rx i think is better to stay with it

Here is an insipring article about the topic Implementing an Event Bus With RxJava

LocalBroadcast

The classic way to do this is to use LocalBroadcastManager but in my aopinion they are a pain

Upvotes: 6

Roger Garzon Nieto
Roger Garzon Nieto

Reputation: 6594

I'm currently using this Pub/Sub pattern with rxjava and enum class.

public enum Events {

  public static PublishSubject <Object> myEvent = PublishSubject.create ();
}

//where you want to publish something

Events.myEvent.onNext(myObject);

//where you want to receive an event

Events.myEvent.subscribe (...); 

Upvotes: 0

mradzinski
mradzinski

Reputation: 624

I'm currently developing a Bus based solely on RxJava. Since you already have RxJava on your project, you can use it for this. You should use a BehaviorSubject and Observable.switchOnNext().

For example:

private BehaviorSubject<Observable<Whatever>> subject = BehaviorSubject.create();

public void post(){
    subject.onNext(...);
}

public Observable<Whatever> subscribe(){
    return Observable.switchOnNext(subject);
}

You should have this as part of a Singleton so the same BehaviorSubject is used. All you have to do is post() from one fragment and subscribe() on the other one or in any other interested fragment or activity. You can have as many subscriptions as you want, plus if you implement it correctly then the last emitted Observable will survive orientation changes.

More info on BehaviorSubject can be found here: https://github.com/ReactiveX/RxJava/wiki/Subject

Upvotes: 0

Biraj Zalavadia
Biraj Zalavadia

Reputation: 28484

Try this way hope it help you.

For Example:

YourService

public class MyService extends Service{

    public static MyServiceListener getMyServiceListener() {
        return MyService.myServiceListener;
    }

    public static void setMyServiceListener(MyServiceListener myServiceListener) {
        MyService.myServiceListener = myServiceListener;
    }

    private static MyServiceListener myServiceListener;


    public interface MyServiceListener{
        void onResult(String response);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();


    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        executeYourTask();
    }

    private void executeYourTask(){

        String result = "SomeResultMaybeFromServer";

        if(getMyServiceListener()!=null){
            getMyServiceListener().onResult(result);
        }
    }
}

YourFragment

    public class MyFragment extends Fragment {




    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {


        View v = null; // some view

        // Start service


        MyService.setMyServiceListener(new MyService.MyServiceListener() {
            @Override
            public void onResult(String response) {

                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // To handle memory/window leaks
                    }
                });

            }
        });

        return v;
    }
}

Upvotes: 0

Booger
Booger

Reputation: 18725

I would suggest using an Event Bus for this sort of thing. It will allow you to send messages to components within your system, without requiring creating special handlers.

Otto is a popular open source library for this, and there are others. http://square.github.io/otto/

Upvotes: 0

Related Questions