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 shorthandonEvent(YourEvent eventInstance)
always dispatches into the main UI threadonEventBackgroundThread(YourEvent eventInstance)
this is most suitable for your problemonEventAsync(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