Service中使用EventBus.post()从后台线程调用函数及应用滑关清理报错问题
我来帮你逐个捋清楚这两个EventBus相关的问题哈:
要让EventBus的订阅方法在后台线程执行,有两种很直接的实现方式:
用ThreadMode指定执行线程:
不管你在Service的哪个线程发送事件,只要在订阅者的处理方法上加上@Subscribe(threadMode = ThreadMode.BACKGROUND)注解,这个方法就会自动在后台线程运行。比如:@Subscribe(threadMode = ThreadMode.BACKGROUND) public void handleCustomEvent(YourEventClass event) { // 这里的代码会在后台线程执行,无需手动处理线程切换 }EventBus会自动帮你完成线程切换,不用自己操心。
直接在后台线程发送事件:
如果你本身就是在Service的后台线程(比如通过HandlerThread、线程池启动的线程)里调用EventBus.getDefault().post(event),那如果订阅者的方法使用默认的ThreadMode.POSTING模式,这个方法就会和发送事件的线程保持一致,也就是在后台线程执行。比如在Service里这么写:new Thread(() -> { // 后台线程业务逻辑 EventBus.getDefault().post(new YourEventClass()); }).start();对应的订阅方法如果没指定ThreadMode,就会在这个后台线程中执行。
你遇到的No subscribers registered for event class错误,本质是当Service发送StopSearching事件时,已经没有任何类(比如你的Activity)注册了该事件的订阅方法——毕竟用户滑掉应用时,Activity大概率已经被销毁,并且完成了EventBus的反注册。
给你几个实用的解决思路:
确认订阅者的注册/反注册时机:
先确保你的Activity在合适的生命周期节点完成注册和反注册,比如:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EventBus.getDefault().register(this); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); }但这只能保证正常流程的注册,滑掉应用时Activity仍会销毁反注册,所以需要配合下面的方法。
发送事件前先检查是否有订阅者:
在Service里发送事件之前,先调用EventBus.getDefault().hasSubscriberForEvent(StopSearching.class)判断,如果存在订阅者再发送,避免触发无订阅者的错误:StopSearching stopEvent = new StopSearching(); if (EventBus.getDefault().hasSubscriberForEvent(stopEvent.getClass())) { EventBus.getDefault().post(stopEvent); }直接在Service的生命周期方法中执行清理:
用户滑掉应用时,系统会调用Service的onTaskRemoved()方法,你可以直接在这里执行清理操作,不需要通过EventBus通知Activity,从根源上避免无订阅者的问题:@Override public void onTaskRemoved(Intent rootIntent) { super.onTaskRemoved(rootIntent); // 在这里执行你的清理逻辑:比如停止搜索、释放资源 stopSelf(); // 清理完成后可以停止Service }检查事件类的访问权限:
你的MyService$StopSearching是内部类,要确保它是public的,不然订阅者可能无法正确引用到这个类,导致订阅失败。比如把内部类改成public:public class StopSearching { // 事件类的内容 }
内容的提问来源于stack exchange,提问作者ashaneen

