【问题标题】:Best practice for eventbus with thread safety具有线程安全的事件总线的最佳实践
【发布时间】:2015-01-06 07:07:43
【问题描述】:

我的应用有用于用户交互的活动和一个后台服务,这是修改数据模型的唯一地方。后台服务侦听用户所做的操作以及来自网络的传入消息。因此可能会出现并发问题,我试图通过使用处理程序来防止这些问题。 对于事件层,我使用 greenrobots Eventbus。

这一切都很好,但我想知道是否有更智能/更快/更少代码扩展(因此更不容易出错)的方式来处理这个用例?

更具体:

  • 有没有办法确保 onEvent 方法的串行执行 没有处理程序?
  • 是否有替代 onEvent 方法的方法 对于每个可能的事件?
  • 有没有更好的模式适合我 在这里做什么?

这是我的方法:

在 oncreate 方法中,我会注册服务(如果有活动,我会在 onstart 中执行此操作)

@Override
public void onCreate() {
    super.onCreate();
    ...
    EventBus.getDefault().register(this);
}

在 onDestroy 中,我再次注销

@Override
public void onDestroy() {
    super.onDestroy();
    ....
    EventBus.getDefault().unregister(this);
}

每当我对传入事件做出反应时,我都希望确保串行执行,因为可能会出现并发问题,因为有来自用户交互以及通过网络来自其他用户的传入事件。所以我决定使用处理程序:

private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            Object receivedEvent = msg.obj;
            if(receivedEvent instanceof EditUser)
            {
                processEditUserBroadcast((EditUser)receivedEvent);
            }           
            else if(receivedEvent instanceof JoinParty)
            {
                processJoinPartyBroadcast((JoinParty)receivedEvent);
            }
            else if(receivedEvent instanceof LeaveParty)
            {
                processLeavePartyBroadcast();
            }
            else if(receivedEvent instanceof SendMessage)
            {
                processSendMessageBroadcast((SendMessage)receivedEvent);
            }
            else if(receivedEvent instanceof ReceivedMessage)
            {
                processReceivedMessageBroadcast((ReceivedMessage)receivedEvent);
            }       
            else if(receivedEvent instanceof Reset)
            {
                processResetBroadcast();
            }
            else if(receivedEvent instanceof ImageDownloadFinished)
            {
                processImageDownloadFinishedBroadcast((ImageDownloadFinished)receivedEvent);
            }
        }
    };  
    return handler;
}

对于每个感兴趣的事件,我确实有一个 onEvent 方法,它除了将事件传递给处理程序以确保通过一个小的“passToHandler”辅助函数串行执行

public void passToHandler(Handler handler, Object object)
{
    Message message = handler.obtainMessage();
    message.obj = object;
    handler.sendMessage(message);
}

public void onEvent(EditUser editUser)
{
    passToHandler(handler,editUser);
}

public void onEvent(JoinParty joinParty)
{
    passToHandler(handler,joinParty);
}

public void onEvent(LeaveParty leaveParty)
{
    passToHandler(handler,leaveParty);
}

public void onEvent(SendMessage sendMessage)
{
    passToHandler(handler,sendMessage);
}

public void onEvent(ReceivedMessage receivedMessage)
{
    passToHandler(handler,receivedMessage);
}

public void onEvent(Reset reset)
{
    passToHandler(handler,reset);
}

public void onEvent(ImageDownloadFinished imageDownloadFinished)
{
    passToHandler(handler,imageDownloadFinished);
}

“过程..”方法是“数据魔法”发生的地方,不应该与我的问题相关。

当然,对于每个可能的事件,我确实创建了一个通常非常苗条的类,如下所示:

public class JoinParty {
    private String partyCode;

    public JoinParty(String partyCode) {
        super();
        this.partyCode = partyCode;
    }
    public String getPartyCode() {
        return partyCode;
    }   
}

【问题讨论】:

    标签: android design-patterns thread-safety handler greenrobot-eventbus


    【解决方案1】:

    感谢您张贴此马蒂亚斯!我认为您提出了一个关于 GreenRobot EventBus 线程安全的非常重要的观点,很容易被它的用户忽略。

    我认为您很有可能走在正确的道路上,尽管我是 GreenRobot EventBus 和 Android(但不是 Java)的新手。如果我正确阅读了 GreenRobot EventBus 源代码,这种方法的另一个可能的好处是,将 SendMessage 事件发布到您的 onEvent() 方法会立即返回(在处理程序上调用 sendMessage 之后)允许 EventBus 继续将其发布到任何其他订阅者毫不拖延地由您的班级实际处理。不过,这可能是也可能不是您想要的。

    使用您给出的方法,您需要确保的另一件事是,如果您采用这样的方法,那么您的类中没有其他公共方法具有您的所有 onEvent() 方法和诸如此类的方法作为 processEditUserBroadcast()。否则,虽然您已确保从 EventBus 接收到的事件的所有处理实际上都是在单个线程上(以串行方式)处理的,但其他一些类可能会在不同的线程上调用此类的公共方法,然后导致你又线程安全问题了。

    如果你知道你确实需要在这个类上支持其他公共方法,那么做你在这里所做的至少可以将所有 onEvent() 方法处理到一个线程上(用于创建线程的 Looper Looper 来自我在 Looper 类的文档中读到的内容),这至少简化了一些事情。然后,您可能还需要对公共方法和所有其他方法(例如 processEditUserBroadcast()这节课。或者,根据这些数据成员是什么以及您的需求,您可能可以通过简单地使其中一些成为易失性、原子性或使用并发集合等来解决问题。这完全取决于读写访问权限需求是,也是这些访问所需的粒度。

    这有帮助吗?对于那些精通 Android、Loopers、Handlers、GreenRobot EventBus 等的人,我说错了吗?

    【讨论】:

    • 另外,我很高兴您发布 Matthias 的原因是,我正在寻找与使用 EventBus 处理此问题的设计模式完全相同的东西,您的帖子将我引导到 Handler 和 Looper 的这是朝着正确方向迈出的非常好的一步。我正在考虑创建自己的事件处理队列,以将事件添加到各种 onEvent 方法中。如果 Handler 类有一些精心设计的模式来合并事件,那可能会很好,但这对于许多 EventBus 用户来说可能是一个很好的起点。
    • 嗨,马克,非常感谢。很高兴听到,我的想法并非完全错误。所有的“processXXX()”方法都只会在这个类中使用,因为它们与传入的事件有严格的联系,并且只能通过发布触发该方法的事件被其他类间接访问。
    • 而且,除了构造函数之外,您没有其他公共方法吗?
    • 正确,至少没有一个行为需要线程安全。
    猜你喜欢
    • 2014-08-12
    • 2019-12-31
    • 2010-10-17
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 2014-06-14
    • 1970-01-01
    相关资源
    最近更新 更多