1

我已经Otto在我最新的 Android 项目中进行了尝试,它确实大大简化了对象之间的通信。但是,我不确定线程​​之间的通信是否存在任何隐藏问题。

这就是我所做的,SingletonBus使用 an创建了一个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
}

我的问题是,它似乎正在工作,但是使用 Otto 与ThreadEnforcer.ANY线程之间进行通信是否存在任何隐藏错误?这种方法有什么问题吗?

4

2 回答 2

5

Otto在发布事件的同一线程中同步调度事件。是否只是为了验证您是否从预期的线程调用方法。断言您仅来自主线程。如果你从后台线程使用and ,那么 bus 会抛出一个运行时异常,警告你不要做错事。基本上没有检查就完成了。您可以从任何线程中调用,但是,正如我已经说过的,您也必须期望订阅者也可以从任何线程中调用。ThreadEnforcerpost()ThreadEnforcer.MAINpost()ThreadEnforcer.MAINpost()ThreadEnforcer.ANYpost()

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

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

希望这可以帮助。

于 2014-11-21T21:36:22.703 回答
1

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

于 2014-11-18T06:07:28.573 回答