I'm using Otto as my event bus in my android application.
I had to make sure that certain events are called in the main thread, or not in the main thread, for that I created my own bus class which uses Otto like so:
class MyEventBus {
private final Bus anyThreadBus;
private final Bus mainThreadBus;
private final Bus notMainThreadBus;
private final Handler mainThreadHandler;
public enum Strategy {
Any,
Main,
NotMain
}
MyEventBus() {
this.anyThreadBus = new Bus(ThreadEnforcer.ANY);
this.mainThreadBus = new Bus(ThreadEnforcer.MAIN);
this.notMainThreadBus = new Bus(ThreadEnforcer.ANY);
this.mainThreadHandler = new Handler(Looper.getMainLooper());
}
public void register(Object object) {
this.register(object, Strategy.Any);
}
public void register(Object object, Strategy strategy) {
switch (strategy) {
case Main:
this.mainThreadBus.register(object);
break;
case NotMain:
this.notMainThreadBus.register(object);
break;
case Any:
default:
this.anyThreadBus.register(object);
}
}
public void unregister(Object object) {
try {
this.anyThreadBus.unregister(object);
} catch (Exception e) {}
try {
this.mainThreadBus.unregister(object);
} catch (Exception e) {}
try {
this.notMainThreadBus.unregister(object);
} catch (Exception e) {}
}
public void post(Object event) {
this.anyThreadBus.post(event);
this.enforceOnMainThread(event);
this.enforceOnNotMainThread(event);
}
public void post(Object event, Strategy strategy) {
switch (strategy) {
case Main:
this.enforceOnMainThread(event);
break;
case NotMain:
this.enforceOnNotMainThread(event);
break;
case Any:
default:
this.anyThreadBus.post(event);
}
}
private void enforceOnNotMainThread(final Object event) {
if (MyEventBus.onMainThread()) {
// MyApplication.pool() returns a shared thread pool for the application
MyApplication.pool().execute(new Runnable() {
#Override
public void run() {
notMainThreadBus.post(event);
}
});
} else {
this.notMainThreadBus.post(event);
}
}
private void enforceOnMainThread(final Object event) {
if (MyEventBus.onMainThread()) {
this.mainThreadBus.post(event);
} else {
this.mainThreadHandler.post(new Runnable() {
#Override
public void run() {
mainThreadBus.post(event);
}
});
}
}
private static boolean onMainThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
}
I have two questions:
In my post methods I post the event on all of the 3 buses, and that's because I don't know if the posted event has a registered class in a certain bus. Is there a way to know? something like:
if (this.anyThreadBus.has(event)) { ... }
Is there a way to do this other than maintaining a map of event classes to registered classes per bus?
Currently each registered class has an enforcement per call it made to register. But it would be best if I couldn't specify the enforcement per method and not for the entire class, something like:
#Subscribe(enforcement = Strategy.Main)
public void handleMyEvent(MyEvent event) { ... }
Can that be done somehow?
Thanks.
Maybe it's time to switch your EventBus implementation?
Check out if that one would make live easier for you:
http://greenrobot.org/eventbus/documentation/delivery-threads-threadmode/
Related
Can somebody tell me what the equivalent of this answer https://stackoverflow.com/a/27312494/9507009 without lambda expression, please ?
Extra: One-shot AsyncTask Example
class InternetCheck extends AsyncTask<Void,Void,Boolean> {
private Consumer mConsumer;
public interface Consumer { void accept(Boolean internet); }
public InternetCheck(Consumer consumer) { mConsumer = consumer; execute(); }
#Override protected Boolean doInBackground(Void... voids) { try {
Socket sock = new Socket();
sock.connect(new InetSocketAddress("8.8.8.8", 53), 1500);
sock.close();
return true;
} catch (IOException e) { return false; } }
#Override protected void onPostExecute(Boolean internet) { mConsumer.accept(internet); }
}
///////////////////////////////////////////////////////////////////////////////////
// Usage
new InternetCheck(internet -> { /* do something with boolean response */ });
Without lambda expression it should be like:
new InternetCheck(new InternetCheck.Consumer() {
#Override
public void accept(Boolean internet) {
if (internet) {
Log.d("TAG", "Internet is connected");
doSomethingOnConnected();
} else {
Log.d("TAG", "Internet is not connected");
doSomethingOnNoInternet();
}
}
}).execute();
///////////////////////////////////////////////////////////////////////////////////
// Usage
new InternetCheck(new InternetCheck.Consumer() {
#Override
public void accept(Boolean internet) { /* do something with boolean response */ }
});
... e.g. would be an equivalent non-lambda representation (as #NongthonbamTonthoi also mentioned).
Addition note to this code-section (as the author)
When using it in an activity context you might wanna check if your App/Activity is in "onPause/onStop"-state though, since the answer/call will be asynchronous and you might depend on it.
I did not mention this in the original answer, since it goes into too much detail, but setting a boolean to true in onResume, setting it to false in onPause, and checking it (e.g.) in our onPostExecute would be good practice in most cases.
(edit) More details / example:
boolean mStopped;
#Override
protected void onStart() {
super.onStart();
mStopped = false;
}
#Override
protected void onStop() {
mStopped = true;
super.onStop();
}
And in the simplest "just to be safe" way, check it within your accept-Method:
new InternetCheck(new InternetCheck.Consumer() {
#Override public void accept(Boolean internet) {
if (mStopped) return;
/* ... */
}
});
OR check it within the AsyncTask (=InternetCheck class) itself (within onPostExecute!), which is obviously only possible if it resides in the same file/class, ...
In short, there are many ways to tackle this, including a custom (abstract) Actvity-class for that, which you have all your activities inherit from; a custom App-class that does something similar (dubious if done wrong!!); or using a ContentProvider (good practice, but a lot of work).
I have one case when I need to return an observable immediately, but then replace this observable with another one.
Here is an example
private Flowable<byte[]> mFlowableStream = Flowable.empty();
#Override
public Flowable<byte[]> startStreamRead() {
bindToService();
return mFlowableStream;
}
And then after binding to service I provide it a callback connection like that
#Override
public void bindToService() {
mAppContext.bindService(new Intent(mAppContext,StreamService.class), mServiceConnection, 0);
}
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mServiceInterection = ((StreamServiceInterection.LocalBinder) binder).getServiceInteractor();
mStreamDisposable = mServiceInterection.getStream()
.subscribe(new Consumer<byte[]>() {
#Override
public void accept(byte[] data) throws Exception {
}
});
}
What I want to do is to somehow replace returned previously mFlowableStream with a new observable that I got from service.
What are possible strategies to implement this ? Maybe I should return some other value, like Future.
Please suggest how to solve this problem
Thanks
You can use Flowable.create instead of Flowable.empty
Then when new data come, just push to flowable.
Like Example
final ArrayList<FlowableEmitter<Integer>> arrEmitter = new ArrayList<>();
Flowable<Integer> futureFlow = Flowable.create(new FlowableOnSubscribe<Integer>() {
#Override
public void subscribe(final FlowableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(2);
arrEmitter.add(e); // hold emitter to use later
}
}, BackpressureStrategy.BUFFER);
futureFlow.subscribe(new ResourceSubscriber<Integer>() {
#Override
public void onNext(Integer integer) {
System.out.println("onNext: " + integer);
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
System.out.println("onComplete");
}
});
// =========== When data come
FlowableEmitter<Integer> holdEmitter = arrEmitter.get(0);
holdEmitter.onNext(3);
Or use you can use **Subject* type according to your need
Understanding RxJava Subject — Publish, Replay, Behavior and Async Subject
Is it possible in Otto to subscribe to events without using #Subscribe annotation ?
In my use case I do not know to which event my object should subscribe to at compile time. I wish to do it at runtime based on certain rules.
I suppose you can use a workaround like this,
public class MainClass {
private EventObserver eventObserver;
public MainClass() {
if(...someCondition...) {
eventObserver = new FirstEventObserver();
} else {
eventObserver = new SecondEventObserver();
}
}
public onEvent(Event event) {
if (event instanceOf FirstEvent) {
... handle event ...
} else if (event instanceOf SecondEvent) {
... handle event ...
}
}
}
public abstract class EventObserver {
protected MainClass mainClass;
public void setMainClass(MainClass mainClass) {
this.mainClass = mainClass;
}
protected void notifyMainClass(Event event) {
if (mainClass != null) {
mainClass.onEvent(event);
}
}
}
public class FirstEventObserver extends EventObserver {
public FirstEventObserver() {
bus.subscribe(this);
}
#Subscribe
public void onEvent(FirstEvent event) {
notifyMainClass();
}
}
public class SecondEventObserver extends EventObserver {
public SecondEventObserver() {
bus.subscribe(this);
}
#Subscribe
public void onEvent(SecondEvent event) {
notifyMainClass();
}
}
public abstract class Event {
}
public abstract class FirstEvent extends Event {
}
public abstract class SecondEvent extends Event {
}
Another workaround, which is a much cleaner solution. You can generate the event at runtime with the type you want.
public class MainClass {
#Subscribe
public void onEvent(Event event) {
if (event.getType() == EventType.FIRST_EVENT) {
... handle event ...
} else if (event.getType() == EventType.SECOND_EVENT) {
... handle event ...
}
}
}
public class Event {
public enum EventType {
FIRST_EVENT,
SECOND_EVENT
}
private EventType eventType;
public Event(EventType eventType) {
this.eventType = eventType;
}
public EventType getType() {
return eventType;
}
}
I created a framework for subscribing to events in runtime with Otto. Instead of having different Model classes for different event types one can have different EventDelegate for different events. These event delegate will just receive and event and pass them on to subscriber classes.
A Typical EventDelegate will look like this
public abstract class OttoEventDelegate {
private OttoEventListener ottoEventListener;
public OttoEventDelegate(OttoEventListener ottoEventListener) {
this.ottoEventListener = ottoEventListener;
}
public void register() {
BaseApplication.getInstance().getBus().register(this);
}
public void unregister() {
BaseApplication.getInstance().getBus().unregister(this);
}
public OttoEventListener getOttoEventListener() {
return ottoEventListener;
}
public void setOttoEventListener(OttoEventListener ottoEventListener) {
this.ottoEventListener = ottoEventListener;
}
}
This Approach is explained in this article. Also if you want to have a look at implementation. Its on github here.
I am migrating from Volley to a custom implementation using Retrofit, but I'm trying to add to my implementation some of the Volley features that I liked, for example
RequestQueue.cancel(String tag)
If the Request has the requested tag, then it's canceled by setting a boolean value, mCanceled, to true. The run method checks this value and returns if it's true.
To be able to reproduce this with Retrofit I should be able to use my custom class implementing Runnable instead of the default one, where I have a mTag and a mCanceled field.
Moreover, Volley was also able to set such flag inside the active Threads and immediately stop them. My cancelAll method, that I've already implemented, just drains the queue to another queue, but isn't able to access the active threads.
Is it possible to achieve the same results with Retrofit and ThreadPoolExecutor?
I think I've found a nicer solution: instead of blocking the Runnable of the requests, I am blocking the Callback execution.
I have extended the Callback interface:
public interface CustomCallbackInterface<T> extends Callback<T> {
public String getTag();
public String setTag(String tag);
public void cancel();
public boolean isCanceled();
}
so that each Callback has a tag and a cancel flag. Then the success method starts with:
public class CustomCallback<ConvertedData> implements CustomCallbackInterface<ConvertedData>{
//failure...
#Override
public void success(ConvertedData cd, Response response) {
if(isCanceled()) return;
// ....
}
}
Every time I make a new request, I store the created CustomCallback inside a List cancel just iterates the list and calls cancel() on the items with the same tag.
I've implemented an easy to use class based on Vektor88 answer
public abstract class CancelableCallback<T> implements Callback<T> {
private static List<CancelableCallback> mList = new ArrayList<>();
private boolean isCanceled = false;
private Object mTag = null;
public static void cancelAll() {
Iterator<CancelableCallback> iterator = mList.iterator();
while (iterator.hasNext()){
iterator.next().isCanceled = true;
iterator.remove();
}
}
public static void cancel(Object tag) {
if (tag != null) {
Iterator<CancelableCallback> iterator = mList.iterator();
CancelableCallback item;
while (iterator.hasNext()) {
item = iterator.next();
if (tag.equals(item.mTag)) {
item.isCanceled = true;
iterator.remove();
}
}
}
}
public CancelableCallback() {
mList.add(this);
}
public CancelableCallback(Object tag) {
mTag = tag;
mList.add(this);
}
public void cancel() {
isCanceled = true;
mList.remove(this);
}
#Override
public final void success(T t, Response response) {
if (!isCanceled)
onSuccess(t, response);
mList.remove(this);
}
#Override
public final void failure(RetrofitError error) {
if (!isCanceled)
onFailure(error);
mList.remove(this);
}
public abstract void onSuccess(T t, Response response);
public abstract void onFailure(RetrofitError error);
}
Usage example
rest.request(..., new CancelableCallback<MyResponse>(TAG) {
#Override
public void onSuccess(MyResponse myResponse, Response response) {
...
}
#Override
public void onFailure(RetrofitError error) {
...
}
});
// if u need to cancel all
CancelableCallback.cancelAll();
// or cancel by tag
CancelableCallback.cancel(TAG);
I seem to be having one of two issues here. Either an error like:
java.lang.IllegalStateException: Event bus [Bus "default"] accessed from non-main thread Looper
Or, if I've managed to "address" that, I simply never receive the event in my subscriber.
Currently, I have a class, cobbled from a few sources, sub-classing Bus:
public class MainThreadBus extends Bus {
private static Bus _bus;
private Handler _handler = new Handler(Looper.getMainLooper());
public MainThreadBus() {
if (_bus == null) {
_bus = new Bus();
}
}
#Override public void register(Object obj) {
_bus.register(obj);
}
#Override public void unregister(Object obj) {
_bus.unregister(obj);
}
#Override public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
_bus.post(event);
} else {
_handler.post(new Runnable() {
#Override public void run() {
_bus.post(event);
}
});
}
}
}
Which is used in an Activity like this:
#Subscribe
public void requestProgressAvailable(RESTRequestProgress progress) {
processProgress(progress);
}
#Override
protected void onResume() {
super.onResume();
_bus = new MainThreadBus();
_bus.register(this);
}
#Override
protected void onPause() {
super.onPause();
_bus = new MainThreadBus();
_bus.unregister(this);
}
And publishing from an IntentService like this:
_bus = new MainThreadBus();
_bus.post(request.createRESTRequestProgress(RESTRequest.STATUS_STARTED));
And the messages are not received. An alternate configuration had me receiving the thread error, so I'm going with this, for now.
So, what am I missing, or doing wrong?
EDIT: Thanks to Andy below for pointing out that my code wasn't acting as I thought it was. The code above now reflects modifications based on that feedback.
Aside from the fact this implementation isn't a Singleton, when getting this error, you can use the threadEnforcer.ANY option in the constructor:
private static final Bus BUS = new Bus(ThreadEnforcer.ANY);
The problem is that your code is not interacting with the same bus instance. Instead of creating a new MainThreadBus in each case, you should access the same bus, for example a singleton obtained from a factory or via injection.