我在我最新的Android项目中尝试了Otto
,它确实简化了对象之间的通信。但是,我不确定Threads与它之间的通信是否存在任何隐藏的问题。
这就是我所做的,使用SingletonBus
创建了enum
,以便可以在任何地方访问总线:
public enum SingletonBus {
INSTANCE;
private static String TAG = SingletonBus.class.getSimpleName();
private Bus bus;
private boolean paused;
private final Vector<Object> eventQueueBuffer = new Vector<>();
private Handler handler = new Handler(Looper.getMainLooper());
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public <T> void postToSameThread(final T event) {
bus.post(event);
}
public <T> void postToMainThread(final T event) {
try {
if(paused) {
eventQueueBuffer.add(event);
} else {
handler.post(new Runnable() {
@Override
public void run() {
try {
bus.post(event);
} catch(Exception e) {
Log.e(TAG, "POST TO MAIN THREAD: BUS LEVEL");
throw e;
}
}
});
}
} catch(Exception e) {
Log.e(TAG, "POST TO MAIN THREAD: HANDLER LEVEL");
throw e;
}
}
public <T> void register(T subscriber) {
bus.register(subscriber);
}
public <T> void unregister(T subscriber) {
bus.unregister(subscriber);
}
public boolean isPaused() {
return paused;
}
public void setPaused(boolean paused) {
this.paused = paused;
if(!paused) {
Iterator<Object> eventIterator = eventQueueBuffer.iterator();
while(eventIterator.hasNext()) {
Object event = eventIterator.next();
postToMainThread(event);
eventIterator.remove();
}
}
}
}
然后我创建了一个Event
,它可以包含一个操作的结果(我还没有为每个操作创建一个事件,但是我会尝试重构它,如果它会变得混乱的话这是必要的):
public class KeyPairCreatedEvent {
private KeyPair keyPair;
public KeyPairCreatedEvent(KeyPair keyPair) {
this.keyPair = keyPair;
}
public KeyPair getKeyPair() {
return keyPair;
}
}
然后我创建并发布了此活动:
@Subscribe
public void keyPairCreate(KeyPairCreateEvent keyPairCreateEvent) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
KeyPair keyPair = keyPairCreator.createKeyPair();
SingletonBus.INSTANCE.getBus().post(new KeyPairCreatedEvent(keyPair));
} catch(...){...}
}
});
thread.start();
}
订阅一个事件以在创建密钥对时获得结果:
@Subscribe
public void keyPairCreated(KeyPairCreatedEvent keyPairCreatedEvent) {
Log.d(MainActivity.class.getName(), "Acquired keypair: " + keyPairCreatedEvent.getKeyPair());
//do stuff
}
我的问题是,它似乎工作正常,但是使用带有ThreadEnforcer.ANY
的Otto在线程之间进行通信会有任何隐藏的错误吗?这种方法有什么问题吗?
答案 0 :(得分:5)
Otto在同一个帖子中发送同步事件,并在其中发布。 ThreadEnforcer
只是验证您从预期的线程调用post()
方法。 ThreadEnforcer.MAIN
仅从主线程断言您post()
。如果您从后台线程使用ThreadEnforcer.MAIN
和post()
,那么总线将抛出运行时异常,警告您不要做错事。使用ThreadEnforcer.ANY
基本上没有检查。您可以从任何线程获得post()
,但是,正如我已经说过的那样,您必须从任何线程调用订阅者。
应用于您的代码意味着KeyPairCreatedEvent
将从后台发布,keyPairCreated(KeyPairCreatedEvent)
订阅者也将在该后台线程中调用。如果两个线程(后台和主线程)处理相同的数据,则必须进行同步,否则可能导致不一致。如果您希望在主线程中提供结果(以避免同步),则需要使用Handler.post()
并从那里调用bus.post()
。
或者,您可以尝试TinyBus,它使用与Otto相同的接口,但即使从后台线程发布事件,也会在主线程中调用订阅者。
希望这有帮助。
答案 1 :(得分:1)
Otto中的调度队列用ThreadLocal包装,因此如果在处理订阅者方法中的已接收事件时考虑了并发性,则没有问题。