Communicating from Thread to Thread using GreenRobot EventBus Communicating from Thread to Thread using GreenRobot EventBus android android

Communicating from Thread to Thread using GreenRobot EventBus


From Greenrobot docs at https://github.com/greenrobot/EventBus

BackgroundThread
Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single background thread that will deliver all its events sequentially. Event handlers using this mode should try to return quickly to avoid blocking the background thread.

Async
Event handler methods are called in a separate thread. This is always independent from the posting thread and the main thread. Posting events never wait for event handler methods using this mode. Event handler methods should use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.

When creating a callback, name suffix need to be added to onEvent as follow:

  • onEventMainThread(YourEvent eventInstance) and shorthand onEvent(YourEvent eventInstance)
    always dispatches into the main UI thread
  • onEventBackgroundThread(YourEvent eventInstance)
    this is most suitable for your problem
  • onEventAsync(YourEvent eventInstance)
    always in a new thread, risky if you dispatch multitude of events from here, can hit a Thread exception easy


I had the same problem and I used Looper, Handler and HandlerThread.

It is my BackgroundHandlerThread class:

import android.annotation.TargetApi;import android.os.Build;import android.os.Handler;import android.os.HandlerThread;import android.os.Message;import android.os.Process;import android.util.Log;import java.lang.reflect.Method;import java.util.UUID;public class BackgroundHandlerThread extends Handler {    private static final String TAG = BackgroundHandlerThread.class.getSimpleName();    private HandlerThread handlerThread;    private Object busHandler;    public BackgroundHandlerThread(HandlerThread handlerThread, Object busHandler) {        super(handlerThread.getLooper());        this.handlerThread = handlerThread;        this.busHandler = busHandler;    }    public void onEvent(Object event) {        Log.d(TAG, "onEvent(Object), thread: " + Thread.currentThread().getId() + ", class: " + event.getClass().getName());        Message message = obtainMessage();        message.obj = event;        sendMessage(message);    }    @Override    public void handleMessage(Message msg) {        Method[] aClassMethods = busHandler.getClass().getDeclaredMethods();        for (Method m : aClassMethods) {            if (m.getName().equals("onHandlerThreadEvent")) {                if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0].equals(msg.obj.getClass())) {                    try {                        m.invoke(busHandler, msg.obj);                    } catch (Exception e) {                        Log.wtf(TAG, e);                    }                }            }        }    }    public boolean quit() {        return handlerThread.quit();    }    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)    public boolean quitSafely() {        return handlerThread.quitSafely();    }    public static class Builder {        private HandlerThread handlerThread;        private Object busHandler;        public Builder(Object busHandler) {            this.busHandler = busHandler;        }        public Builder setHandlerThread(HandlerThread handlerThread) {            this.handlerThread = handlerThread;            return this;        }        public BackgroundHandlerThread build() {            if (handlerThread == null) {                handlerThread = new HandlerThread("BackgroundHandlerThread: " + UUID.randomUUID().toString(), Process.THREAD_PRIORITY_BACKGROUND);            }            if (!handlerThread.isAlive()) {                handlerThread.start();            }            return new BackgroundHandlerThread(handlerThread, busHandler);        }    }}

I used it in my service but BackgroundHandlerThread object can be bind to any object.

import android.app.Service;import android.bluetooth.BluetoothDevice;import android.content.Context;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;import de.greenrobot.event.EventBus;public class DeviceService extends Service {    private static final String TAG = DeviceService.class.getSimpleName();    private BluetoothDevice bluetoothDevice;    private BackgroundHandlerThread handlerThread;    private boolean connected = false;    //region Lifecycle    @Override    public void onCreate() {        super.onCreate();        Log.d(TAG, "onCreate, thread: " + Thread.currentThread().getId());        handlerThread = new BackgroundHandlerThread.Builder(this).build();        EventBus.getDefault().register(handlerThread);    }    @Override    public void onDestroy() {        EventBus.getDefault().unregister(handlerThread);        handlerThread.quit();        super.onDestroy();    }    //endregion    public void onHandlerThreadEvent(ConnectToDeviceEvent event) {        Log.d(TAG, "onHandlerThreadEvent, thread: " + Thread.currentThread().getId());        connected = true;        bluetoothDevice = event.device;        EventBus.getDefault().post(new ConnectionStateChangedEvent(bluetoothDevice, connected));    }    //region Static manipulation    public static void startService(Context context) {        Intent intent = new Intent(context, DeviceBinder.class);        context.startService(intent);    }    //endregion}

And activity class:

import android.app.Activity;import android.bluetooth.BluetoothAdapter;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.util.Log;import de.greenrobot.event.EventBus;public class MainActivity extends Activity {    private static final String TAG = MainActivity.class.getSimpleName();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        findViewById(R.id.startButton).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Log.d(TAG, "onStartClick, thread: " + Thread.currentThread().getId());                EventBus.getDefault().post(new ConnectToDeviceEvent(application.getCurrentStateProvider().getDevice()));            }        });        DeviceService.startService(this);    }    @Override    protected void onStart() {        super.onStart();        EventBus.getDefault().register(this);    }    @Override    protected void onStop() {        EventBus.getDefault().unregister(this);        super.onStop();    }    public void onEventMainThread(ConnectionStateChangedEvent event) {        Log.d(TAG, "onEventMainThread(ConnectionStateChangedEvent), thread: " + Thread.currentThread().getId());    }}

Log output:

D/MainActivity: onStartClick, thread: 1D/BackgroundHandlerThread: onEvent(Object), thread: 1, class: ConnectToDeviceEventD/DeviceService: onHandlerThreadEvent, thread: 4399D/BackgroundHandlerThread: onEvent(Object), thread: 4399, class: ConnectionStateChangedEventD/MainActivity: onEventMainThread(ConnectionStateChangedEvent), thread: 1

Similar: Best practice for eventbus with thread safety