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: 1
D/BackgroundHandlerThread: onEvent(Object), thread: 1, class: ConnectToDeviceEvent
D/DeviceService: onHandlerThreadEvent, thread: 4399
D/BackgroundHandlerThread: onEvent(Object), thread: 4399, class: ConnectionStateChangedEvent
D/MainActivity: onEventMainThread(ConnectionStateChangedEvent), thread: 1
Similar: Best practice for eventbus with thread safety