Общение из потока в поток с помощью GreenRobot EventBus

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

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

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

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

Это возможно?

Пример:

  • mainThread
  • backGroundThread1
  • backGroundThread2

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

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

Есть идеи?

2 ответа

Из документации Greenrobot по адресу https://github.com/greenrobot/EventBus

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

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

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

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

У меня была такая же проблема, и я использовал 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 с безопасностью потоков

Другие вопросы по тегам