Связь от потока к потоку с помощью GreenRobot EventBus

Только что началось с GreenRobot EventBus.

Есть только одна вещь, которая мешает мне бороться: как вы общаетесь между разными потоками, так что метод onEventXY() фактически также выполняется в подписанной теме.

Кажется, что когда вы публикуете событие, метод onEvent подписчиков вызывается из того же потока, что и поток, на котором было отправлено событие. Это не то, что я хочу.

Я хочу использовать EventBus для межпоточной связи таким образом, что фактический полученный объект события обрабатывается методом onEvent в потоке, который получил объект события.

Возможно ли это?

Пример:

  • mainThread
  • backGroundThread1
  • backGroundThread2

MainThread отправляет событие на EventBus, backGroundThread1 получает его в свой метод onEventXY() и выполняет код в своем собственном потоке (устанавливает некоторую переменную), backGroundThread2 получает его в свой метод onEventXY и выполняет код внутри своего потока (устанавливает некоторые переменная).

Если это невозможно (пока), я обречен использовать очереди потоков, такие как BlockingQueue, которые сложнее реализовать.

Любые идеи?

Ответ 1

Из документов Greenrobot на https://github.com/greenrobot/EventBus

BackgroundThread
Абонент будет вызываться в фоновом потоке. Если поток публикации не является основным потоком, методы обработчика событий будут вызываться непосредственно в потоке проводки. Если поток проводки является основным потоком, EventBus использует один фоновый поток, который будет передавать все его события последовательно. Обработчики событий, использующие этот режим, должны попытаться быстро вернуться, чтобы избежать блокировки фонового потока.

Async
Методы обработчика событий вызываются в отдельном потоке. Это всегда зависит от потока проводки и основного потока. События в проводке никогда не ждут методов обработчика событий, используя этот режим. Методы обработчика событий должны использовать этот режим, если их выполнение может занять некоторое время, например. для доступа к сети. Избегайте одновременного запуска большого количества длинных асинхронных методов обработчика, чтобы ограничить количество одновременных потоков. EventBus использует пул потоков для эффективного повторного использования потоков из завершенных уведомлений обработчика асинхронных событий.

При создании обратного вызова суффикс имени необходимо добавить в onEvent следующим образом:

  • onEventMainThread(YourEvent eventInstance) и сокращенный onEvent(YourEvent eventInstance)
    всегда отправляется в основной поток пользовательского интерфейса.
  • onEventBackgroundThread(YourEvent eventInstance)
    это наиболее подходит для вашей проблемы.
  • onEventAsync(YourEvent eventInstance)
    всегда в новом потоке, рискованном, если вы отправляете множество событий отсюда, может легко удалять исключение потока

Ответ 2

У меня была та же проблема, и я использовал Looper, Handler и HandlerThread.

Это мой класс BackgroundHandlerThread:

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);
        }
    }
}

Я использовал его в своей службе, но объект BackgroundHandlerThread может быть привязан к любому объекту.

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
}

И класс активности:

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());
    }

}

Выход журнала:

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

Аналогично: Лучшая практика для eventbus с безопасностью потоков