具有线程安全的事件总线的最佳实践



我的应用具有用于用户交互的活动和一个后台服务,这是修改数据模型的唯一位置。后台服务侦听用户执行的操作以及来自网络的传入消息。因此,可能会出现并发问题,我试图通过使用处理程序来防止这些问题。对于事件层,我使用绿色机器人事件总线。

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

更具体地说:

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

这是我的方法:

在 oncreate 方法中,我确实注册了服务(如果是活动,我在 onstart 中执行此操作)

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

在销毁中,我再次取消注册

@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;
    }   
}

感谢您发布此马蒂亚斯! 我认为您提出了一个关于GreenRobot EventBus线程安全性的非常重要的观点,用户很容易错过这一点。

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

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

如果你知道你确实需要在这个类上支持其他公共方法,那么执行你在这里所做的工作至少可以将所有 onEvent() 方法处理到单个线程上(Looper 的线程的 Looper 用于从我在 Looper 类的文档中创建 Looper 的线程),这至少简化了一些事情。 然后,您可能还需要对公共方法和所有其他方法(如 processEditUserBroadcast())应用一些同步,以便在此类上具有其他公共方法时保证从多个线程安全访问类的数据成员。 或者,根据这些数据成员是什么以及您的需求是什么,您可能只需使其中一些成员易失性、原子性或使用并发集合等即可。 这完全取决于读写访问需求以及这些访问所需的粒度。

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

最新更新