Otto/EventBus across multiple processes
Asked Answered
T

4

14

Is it possible to post event in one process (for example inside SyncAdapter which has android:process=":sync" manifest attribute) and receive it in another (inside regular app UI) with Otto or EventBus?

I know that Intent and BroadcastReceiver work just fine for communication across multiple processes but I would like to have simplicity and flexibility with Otto/EventBus.

Trouper answered 29/8, 2014 at 12:20 Comment(0)
F
16

No, that is not possible, as Otto, greenrobot's EventBus, and LocalBroadcastManager are all in-process solutions.

You might consider simply removing the android:process attribute from the manifest, so it all runs in one process.

Fordo answered 29/8, 2014 at 12:25 Comment(10)
But then I would lose sync functionality if app is closed (because :sync process is common shared one and is working in background)? "The attribute android:exported="true" allows processes other than your app (including the system) to access the Service. The attribute android:process=":sync" tells the system to run the Service in a global shared process named sync." - developer.android.com/training/sync-adapters/…Trouper
@svenkapudija: You do not need to use android:process=":sync" to use a SyncAdapter. "because :sync process is common shared one and is working in background" -- it would be common between several of your components that use android:process=":sync". It would not be in common with anything else on the device.Fordo
Also thought that the :sync was required for the SyncAdapter to run in the background. Turns out that is not the case, and by removing it you can sync in the background while still let your UI know (through Otto or Broadcasts) that something happenedPinchbeck
@Fordo Is there any solution for making communication between 2 different process other than BroadcastReceiver? I'm using foreground service(which has to run in different process because of platform bugs.) and want to communicate with activity.Abraxas
@VishalKale: Have the activity bind to the service and provide an AIDL-defined callback. Or, have the activity send a PendingIntent, ResultReceiver, or Messenger to the service as an extra on the startService() Intent. Or, have the process with the service also have a ContentProvider that the activity uses.Fordo
@Fordo I'm starting Service from 1 fragment and want to receive updates into another fragments(host activity is also different). So the options mentioned by you would not work for me. That means I will have to use BroadcastReceiver?Abraxas
@VishalKale: "So the options mentioned by you would not work for me" -- sure they would. Everything I listed would work, though which would be best depends a lot on what sort of data you are trying to pass. Once the IPC has gotten the information back into the process that has your activities, use an event bus to route the data to whatever needs it.Fordo
@Fordo Fragment that calls startService() ends immediately after starting service. Now I want to receive the updates from that service into another fragments. So how can I get the result from the service since I cannot pass the ResultReceiver or Messanger as the fragment that started the service will be destroyed immediately.Abraxas
@VishalKale: Use a PendingIntent. If you have additional concerns in this area, please ask a fresh Stack Overflow question.Fordo
@Fordo Help required. I have created fresh question. Here's the link #38937143Abraxas
W
1

No, but you can use transit. For example using BroadcastReceiver: In one process, send a broadcast with your data, then through the interior of BroadcastReceiver onReceive methods, post a otto event.

Like my codes:

public class ReceiveMessageBroadcastReceiver extends BroadcastReceiver {

    public static final String ACTION_RECEIVE_MESSAGE
            = "me.drakeet.xxxxxx.ACTION_RECEIVE_MESSAGE";
    public static final String AGR_MESSAGE = "AGR_MESSAGE";


    // this method can be called in other processes
    public static void sendBroadcast(Context context, MessageContent content) {
        Intent intent = new Intent();
        intent.setAction(ACTION_RECEIVE_MESSAGE);
        intent.putExtra(AGR_MESSAGE, content);
        context.sendBroadcast(intent);
    }


    // this method will run in your default process, so you can post otto events to your
    // default process
    @Override public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(ACTION_RECEIVE_MESSAGE)) {
            MessageContent content = intent.getParcelableExtra(AGR_MESSAGE);
            Otto.getSeat().post(new PlayMessageReceivedEvent(content));
        }
    }
}
Wier answered 17/12, 2015 at 14:18 Comment(0)
A
1

I know this question is a bit old, but there seems to be a library that claims it can handle a cross-process communication with an event-bus/Rx style architecture.

https://github.com/edisonw/PennStation

Disclaimer: I have not tried this, just found it and it claims to do what this question is asking.

Arleta answered 6/7, 2016 at 18:32 Comment(0)
C
0

I know the answer was already accepted but I thought I'd write about how I solved the problem in case anyone runs across this and is curious how the code might look like.

If you are using Otto, I followed the accepted answer above by removing the android:process from the manifest. I also followed the answer provided here How to send event from Service to Activity with Otto event bus?, where a Bus exception was being thrown for not being run on the main thread. Therefore I combined the two answers and created a bus to be executed on the main thread as per the link above.

public class MainThreadBus extends Bus {
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    @Override
    public void post(final Object event) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            super.post(event);
        } else {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    MainThreadBus.super.post(event);
                }
            });
        }
    }
}

I then created a Bus singleton that can used anywhere in the application:

public final class BusProvider {
    private static final MainThreadBus BUS = new MainThreadBus();

    public static MainThreadBus getInstance() {
        return BUS;
    }

    private BusProvider() {
    }
}

In my SyncAdapter I used the following code initiate the event, BusProvider.getInstance().post(event); and in my application fragment I simply subscribed to the event.

This worked perfectly fine when the application was in the foreground as well as when the sync adapter was initiated in the background after the application was swiped away.

Cacique answered 24/7, 2015 at 2:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.