You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将okhttp-eventsource的BackgroundEventHandler值传递到主线程?

解决okhttp-eventsource在Android主线程接收SSE消息的问题

问题原因

你调用bes.getEventSource().messages().forEach()时触发NetworkOnMainThreadException,是因为messages()返回的是阻塞式可迭代序列——主线程执行forEach会被阻塞等待新消息,而Android严格禁止主线程执行网络/阻塞操作,因此抛出异常。

解决方案

直接在BackgroundEventHandler的onMessage回调中,将消息切换到主线程处理,无需调用messages()方法。以下是两种可靠实现方式:

方式1:使用主线程Handler

修改BackgroundEventHandler,通过Handler将任务投递到主线程:

public class BackgroundEventHandler implements EventHandler {
    private static final String TAG = "BackgroundEventHandler";
    private final Handler mainHandler;

    // 传入主线程Handler
    public BackgroundEventHandler(Handler mainHandler) {
        this.mainHandler = mainHandler;
    }

    @Override
    public void onMessage(String event, MessageEvent messageEvent) {
        Log.d(TAG, messageEvent.getData().toString());
        // 将消息处理逻辑投递到主线程
        mainHandler.post(() -> {
            // 在这里执行主线程操作:解析JSON、更新UI等
            String jsonData = messageEvent.getData();
            Log.d("MainThread", "Received SSE message: " + jsonData);
            // 示例:解析JSON
            // YourJsonParser.parse(jsonData);
        });
    }

    // 实现其他EventHandler方法(onOpen、onError等)
    @Override
    public void onOpen() {}

    @Override
    public void onError(Throwable t) {}
}

在MainActivity中初始化并使用:

private void newThingCreated() throws MalformedURLException {
    // 获取主线程Handler
    Handler mainHandler = new Handler(Looper.getMainLooper());
    BackgroundEventHandler tcHandler = new BackgroundEventHandler(mainHandler);

    BackgroundEventSource bes = new BackgroundEventSource.Builder(tcHandler,
            new EventSource.Builder(
                    ConnectStrategy.http(explorer.createQueryUrl("/events/thing_created"))
                            .connectTimeout(10, TimeUnit.SECONDS)
            )
    )
            .threadPriority(Thread.MAX_PRIORITY)
            .build();
    bes.start();
    // 移除原有的messages().forEach代码
}

方式2:使用Activity的runOnUiThread(需避免内存泄漏)

通过弱引用持有Activity,在onMessage中调用runOnUiThread:

public class BackgroundEventHandler implements EventHandler {
    private static final String TAG = "BackgroundEventHandler";
    private final WeakReference<MainActivity> activityRef;

    public BackgroundEventHandler(MainActivity activity) {
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    public void onMessage(String event, MessageEvent messageEvent) {
        Log.d(TAG, messageEvent.getData().toString());
        MainActivity activity = activityRef.get();
        // 检查Activity是否存活,避免内存泄漏和空指针
        if (activity != null && !activity.isFinishing()) {
            activity.runOnUiThread(() -> {
                // 主线程处理逻辑
                String jsonData = messageEvent.getData();
                // 处理JSON、更新UI等操作
            });
        }
    }

    // 实现其他EventHandler方法
    @Override
    public void onOpen() {}

    @Override
    public void onError(Throwable t) {}
}

在MainActivity中使用:

private void newThingCreated() throws MalformedURLException {
    BackgroundEventHandler tcHandler = new BackgroundEventHandler(this);

    BackgroundEventSource bes = new BackgroundEventSource.Builder(tcHandler,
            new EventSource.Builder(
                    ConnectStrategy.http(explorer.createQueryUrl("/events/thing_created"))
                            .connectTimeout(10, TimeUnit.SECONDS)
            )
    )
            .threadPriority(Thread.MAX_PRIORITY)
            .build();
    bes.start();
    // 移除原有的messages().forEach代码
}

原理说明

BackgroundEventSource会在后台线程执行EventHandler的所有回调方法,因此onMessage本身运行在后台线程。我们通过Handler.post()或runOnUiThread()将消息处理逻辑投递到主线程的消息队列,既遵守了Android的线程模型,又能安全地在主线程处理SSE消息。

内容的提问来源于stack exchange,提问作者simonetassi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 07:23:19