Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Guava's EventBus, possible to run subscriber code on thread that created the bus?

Using Guava's EventBus, I want to be able to post from a background thread (called "background") to a specific thread (in this case, thread "main") that updates the UI. I thought the following would work, but this calls the subscriber code from the background thread:

package com.example;

import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.common.util.concurrent.MoreExecutors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EventBusTester {

    private static final Logger log = LoggerFactory.getLogger(EventBusTester.class);

    public static void main(String... args) {
        new EventBusTester().run();
    }

    private void run() {
        log.info("Starting on thread {}.", Thread.currentThread().getName());

        final EventBus eventBus = new AsyncEventBus(MoreExecutors.sameThreadExecutor());
        eventBus.register(this);

        Thread background = new Thread(new Runnable() {
            @Override
            public void run() {
                long now = System.currentTimeMillis();
                eventBus.post(now);
                log.info("Posted {} to UI on thread {}.", now, Thread.currentThread().getName());
            }
        }, "background");
        background.start();
    }

    @Subscribe
    public void updateUi(Long timestamp) {
        log.info("Received {} on UI on thread {}.", timestamp, Thread.currentThread().getName());
    }
}

This prints the following:

02:20:43.519 [main] INFO  com.example.EventBusTester - Starting on thread main.
02:20:43.680 [background] INFO  com.example.EventBusTester - Received 1387848043678 on UI on thread background.
02:20:43.680 [background] INFO  com.example.EventBusTester - Posted 1387848043678 to UI on thread background.

So my questions are:

  1. Is it possible to do what I want, e.g. with an ExecutorService I've somehow missed, or writing a custom ExecutorService, or
  2. Do I need some other library to accomplish this? E.g. Square's Otto (because I'll be using this on Android as well).

I'd rather stay with pure Guava, though.

Thanks!

like image 375
Markus Avatar asked Feb 15 '23 07:02

Markus


2 Answers

If you use an EventBus instance then the @Subscribe method will be executed on the same thread that posted the event.

If you want to do something different then use an AsyncEventBus where you can provide an Executor to define the exact behavior in case of an event gets posted.

For instance, on Android to make every @Subscribe method run on the main thread you can do the following:

EventBus eventBus = new AsyncEventBus(new Executor() {

    private Handler mHandler;

    @Override
    public void execute(Runnable command) {
        if (mHandler == null) {
            mHandler = new Handler(Looper.getMainLooper());
        }
        mHandler.post(command);
    }
});

The Looper.getMainLooper() returns the application's main looper, which lives on the main thread of the application.

like image 166
Zsolt Safrany Avatar answered Feb 17 '23 03:02

Zsolt Safrany


  1. In UI applications, there is a thread running an event dispatch loop. which is processing user input events and calling handlers. Typically, UI framework provides some way to execute your code in this thread, like SwingUtilities.invokeLater(Runnable)
  2. AsyncEventBus allows you to pass pass Executor, which will be calling UI-framework specific function for that.
  3. There are a few questions here related to executing UI code from a worker thread on android.
like image 28
Alex Panchenko Avatar answered Feb 17 '23 01:02

Alex Panchenko