使用Otto进行线程之间的通信:它会导致任何问题吗?

时间:2014-11-16 10:36:38

标签: java android multithreading otto

我在我最新的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在线程之间进行通信会有任何隐藏的错误吗?这种方法有什么问题吗?

2 个答案:

答案 0 :(得分:5)

Otto在同一个帖子中发送同步事件,并在其中发布。 ThreadEnforcer只是验证您从预期的线程调用post()方法。 ThreadEnforcer.MAIN仅从主线程断言您post()。如果您从后台线程使用ThreadEnforcer.MAINpost(),那么总线将抛出运行时异常,警告您不要做错事。使用ThreadEnforcer.ANY基本上没有检查。您可以从任何线程获得post(),但是,正如我已经说过的那样,您必须从任何线程调用订阅者。

应用于您的代码意味着KeyPairCreatedEvent将从后台发布,keyPairCreated(KeyPairCreatedEvent)订阅者也将在该后台线程中调用。如果两个线程(后台和主线程)处理相同的数据,则必须进行同步,否则可能导致不一致。如果您希望在主线程中提供结果(以避免同步),则需要使用Handler.post()并从那里调用bus.post()

或者,您可以尝试TinyBus,它使用与Otto相同的接口,但即使从后台线程发布事件,也会在主线程中调用订阅者。

希望这有帮助。

答案 1 :(得分:1)

Otto中的调度队列用ThreadLocal包装,因此如果在处理订阅者方法中的已接收事件时考虑了并发性,则没有问题。