How to send event from Service to Activity with Otto event bus?
Asked Answered
K

7

58

Simple BusProvider.getInstance().post() bring exception not main thread. How to send event from Service to Activity with Otto event bus?

Katinakatine answered 15/3, 2013 at 11:53 Comment(2)
By default Otto uses the MAIN thread (see thread enforcement: square.github.com/otto). Are you trying to post FROM another thread? Include example code and your stack trace.Lydgate
Another way of achieving this is to use runOnUiThread, provided there is access to an Activity. Not very clean but will do the job.Unclose
V
122

To post from any thread (main or background) and receive on the main thread, try something like

public class MainThreadBus extends Bus {
  private final Handler mHandler = new Handler(Looper.getMainLooper());

  @Override
  public void post(final Object event) {
    if (Looper.myLooper() == Looper.getMainLooper()) {
      super.post(event);
    } else {
      mHandler.post(new Runnable() {
        @Override
        public void run() {
          MainThreadBus.super.post(event);
        }
      });
    }
  }
}

Note: credit goes to Jake Wharton and "pommedeterresaute" at https://github.com/square/otto/issues/38 for the general approach. I just implemented it with a wrapper class rather than a subclass.

Viewy answered 15/3, 2013 at 13:9 Comment(9)
Thanks. So as of now, does Bus bus2 = new Bus(ThreadEnforcer.MAIN); resolve it?Omidyar
For me ThreadEnforcer.MAIN did not resolve it, but the code provided here.Lyon
@platzhirsch from the doc: If you are not concerned on which thread interaction is occurring, instantiate a bus instance with ThreadEnforcer.ANYSyndicate
@gdrc Thanks for catching up on this. After looking into the source code it became clear. I thought ThreadEnforcer.ANY is the default.Lyon
Why are you using a wrapper and a subclass? Very confusing.Fransis
Any idea if this would work on a Wear device using WearableListenerService?Obryant
why isn't this built into the library? Its a pretty common use case.Kab
Not sure, but this code causes an out of memory error for me.Divisor
what is best approach to handle if event is null?Inharmonious
K
20

To post from any thread (main or background) and receive on the main thread, use the following MainThreadBus instead of a vanilla Bus

public class MainThreadBus extends Bus {
     private final Handler handler = new Handler(Looper.getMainLooper());

     @Override public void post(final Object event) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            super.post(event);
        } else {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    MainThreadBus.super.post(event);
                }
            });
        }
    }
}

This is based on Andy Dennie's answer.

There is no need to both extend and wrap a Bus object, do one or the other. In Dennie's answer, which is effectively a wrapper, the Bus base class is just being used like an interface, all the functionality is overwritten.

It would work even if you removed the Bus base class unless you happened to reference the MainThreadBus via a Bus reference.

Korella answered 27/2, 2014 at 9:13 Comment(1)
My answer implemented an interface which was part of Otto at the time, but has since been removed (and someone edited my answer to make it extend rather than implement); that's why it looks odd now. In my example I wanted to wrap another bus that was not limited to being used on the main thread only.Viewy
S
3

Or simply do this if you're sure that you're posting from a Non-main thread:

new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        mBus.post(new myEvent());
                    }
                });
Santo answered 25/11, 2016 at 4:6 Comment(0)
D
2

bus = new Bus(ThreadEnforcer.ANY); is the clear solution to this problem. Its all you have to do.

Demodulation answered 4/4, 2016 at 10:55 Comment(0)
P
0

Best implementation custom bus class for me

public class AndroidBus extends Bus {
    private final Handler mainThread = new Handler(Looper.getMainLooper());

    @Override
    public void post(final Object event) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            super.post(event);
        } else {
            mainThread.post(() -> AndroidBus.super.post(event));
        }
    }
}
Perception answered 14/4, 2016 at 10:31 Comment(0)
I
0

I did it simply:

Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
                bus.post(event);
            }

        });
Infantilism answered 19/1, 2017 at 14:1 Comment(0)
I
-1

Just create the BasicBus with ThreadEnforcer.NONE to post event from non-main threads. The mentioned ThreadEnforcer.MAIN is exactly the opposite (and the default), which accepts only posts from the main-thread.

Include answered 22/4, 2015 at 21:55 Comment(1)
It's unlikely that this is a usable answer. The OP says "... to Activity ...", which implies to me that the processing needs to be done on the UI or main thread.Vanden

© 2022 - 2024 — McMap. All rights reserved.