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

Kotlin连接Supabase Realtime WebSocket遇WebSocketCapability不支持错误

问题:Supabase Realtime连接报错"Engine doesn't support WebSocketCapability"

我正在开发一款即时通讯应用,要求用户点击发送按钮后消息能实时展示。应用的Activity代码用Java编写,Supabase连接逻辑用Kotlin实现。尝试连接Supabase Realtime数据库时,出现如下错误:

Error while trying to connect to realtime websocket. Trying again in 7s
URL: wss://ucqbegvfrgwmsxharvgh.supabase.co/realtime/v1/websocket?apikey=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVjcWJlZ3Zmcmd3bXN4aGFydmdoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDE1Mjc0NDMsImV4cCI6MjA1NzEwMzQ0M30.pMQ3W4I3OHO3c12QvkVxg-HMuLx0kG3Bb-uepxnjkfc&vsn=1.0.0
java.lang.IllegalArgumentException: Engine doesn't support WebSocketCapability
at io.ktor.client.engine.HttpClientEngine$DefaultImpls.checkExtensions(HttpClientEngine.kt:189)
at io.ktor.client.engine.HttpClientEngine$DefaultImpls.access$checkExtensions(HttpClientEngine.kt:36)
at io.ktor.client.engine.HttpClientEngine$install$1.invokeSuspend(HttpClientEngine.kt:152)
at io.ktor.client.engine.HttpClientEngine$install$1.invoke(Unknown Source:15)
at io.ktor.client.engine.HttpClientEngine$install$1.invoke(Unknown Source:4)
at io.ktor.util.pipeline.DebugPipelineContext.proceedLoop(DebugPipelineContext.kt:79)
at io.ktor.util.pipeline.DebugPipelineContext.proceed(DebugPipelineContext.kt:57)
at io.ktor.util.pipeline.DebugPipelineContext.execute$ktor_utils(DebugPipelineContext.kt:63)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:92)
at io.ktor.client.plugins.HttpSend$DefaultSender.execute(HttpSend.kt:132)
at io.ktor.client.plugins.api.Send$Sender.proceed(CommonHooks.kt:47)
at io.ktor.client.plugins.HttpTimeoutKt$HttpTimeout$3$1.invokeSuspend(HttpTimeout.kt:168)
at io.ktor.client.plugins.HttpTimeoutKt$HttpTimeout$3$1.invoke(Unknown Source:17)
at io.ktor.client.plugins.HttpTimeoutKt$HttpTimeout$3$1.invoke(Unknown Source:6)
at io.ktor.client.plugins.api.Send$install$1.invokeSuspend(CommonHooks.kt:52)
at io.ktor.client.plugins.api.Send$install$1.invoke(Unknown Source:15)
at io.ktor.client.plugins.api.Send$install$1.invoke(Unknown Source:6)
at io.ktor.client.plugins.HttpSend$InterceptedSender.execute(HttpSend.kt:110)
at io.ktor.client.plugins.api.Send$Sender.proceed(CommonHooks.kt:47)
at io.ktor.client.plugins.HttpRedirectKt$HttpRedirect$2$1.invokeSuspend(HttpRedirect.kt:103)
at io.ktor.client.plugins.HttpRedirectKt$HttpRedirect$2$1.invoke(Unknown Source:17)
at io.ktor.client.plugins.HttpRedirectKt$HttpRedirect$2$1.invoke(Unknown Source:6)
at io.ktor.client.plugins.api.Send$install$1.invokeSuspend(CommonHooks.kt:52)
at io.ktor.client.plugins.api.Send$install$1.invoke(Unknown Source:15)
at io.ktor.client.plugins.api.Send$install$1.invoke(Unknown Source:6)
at io.ktor.client.plugins.HttpSend$InterceptedSender.execute(HttpSend.kt:110)
at io.ktor.client.plugins.api.Send$Sender.proceed(CommonHooks.kt:47)
at io.ktor.client.plugins.HttpCallValidatorKt$HttpCallValidator$2$2.invokeSuspend(HttpCallValidator.kt:128)
at io.ktor.client.plugins.HttpCallValidatorKt$HttpCallValidator$2$2.invoke(Unknown Source:13)
at io.ktor.client.plugins.HttpCallValidatorKt$HttpCallValidator$2$2.invoke(Unknown Source:6)
at io.ktor.client.plugins.api.Send$install$1.invokeSuspend(CommonHooks.kt:52)
at io.ktor.client.plugins.api.Send$install$1.invoke(Unknown Source:15)
at io.ktor.client.plugins.api.Send$install$1.invoke(Unknown Source:6)
at io.ktor.client.plugins.HttpSend$InterceptedSender.execute(HttpSend.kt:110)
at io.ktor.client.plugins.HttpSend$Plugin$install$1.invokeSuspend(HttpSend.kt:98)
at io.ktor.client.plugins.HttpSend$Plugin$install$1.invoke(Unknown Source:15)
at io.ktor.client.plugins.HttpSend$Plugin$install$1.invoke(Unknown Source:4)
at io.ktor.util.pipeline.DebugPipelineContext.proceedLoop(DebugPipelineContext.kt:79)
at io.ktor.util.pipeline.DebugPipelineContext.proceed(DebugPipelineContext.kt:57)
at io.ktor.util.pipeline.DebugPipelineContext.proceedWith(DebugPipelineContext.kt:42)
at io.ktor.client.plugins.websocket.WebSockets$Plugin$install$1.invokeSuspend(WebSockets.kt:188)
at io.ktor.client.plugins.websocket.WebSockets$Plugin$install$1.invoke(Unknown Source:13)
at io.ktor.client.plugins.websocket.WebSockets$Plugin$install$1.invoke(Unknown Source:4)

但同时日志输出显示:

System.out      trying to connect
System.out      connected
System.out      subscribed
System.out      channel status: kotlinx.coroutines.flow.ReadonlyStateFlow@a379d71

以下是调用Supabase的方法及SupabaseClient代码:

fun subscribeToMessages(chatroomID: String, listener: MessageListener) {
    val scope = CoroutineScope(Dispatchers.IO)
    GlobalScope.launch(Dispatchers.IO) {
        try {
            println("trying to connect")
            val channel = supabase.channel("messages")
            supabase.realtime.connect()

            println("connected")

            val changeFlow =
                channel.postgresChangeFlow<PostgresAction.Insert>(schema = "public") {
                    table = "messages"
                }

            changeFlow.onEach {
                println("record: ${it.record}")
            }.launchIn(scope)

            channel.subscribe()
            println("subscribed")
            println("channel status: ${channel.status}")

        } catch (e: Exception) {
            println("Subscription Initialization Error: ${e.message}")
            e.printStackTrace()
        }
    }
}
val supabase = createSupabaseClient(
    supabaseUrl = "https://ucqbegvfrgwmsxharvgh.supabase.co",
    supabaseKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVjcWJlZ3Zmcmd3bXN4aGFydmdoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDE1Mjc0NDMsImV4cCI6MjA1NzEwMzQ0M30.pMQ3W4I3OHO3c12QvkVxg-HMuLx0kG3Bb-uepxnjkfc"
) {
    install(Postgrest)
    install(Auth) {
        autoSaveToStorage = true
        autoLoadFromStorage = true
    }
    install(Storage)
    install(Realtime)
    defaultLogLevel = LogLevel.DEBUG
}

解决方案

这个错误核心原因是当前使用的Ktor客户端引擎不支持WebSocket能力,Supabase Realtime依赖Ktor的WebSocket实现来建立实时连接,解决步骤如下:

1. 添加支持WebSocket的Ktor引擎依赖

在Module级别的build.gradle中添加OkHttp引擎依赖(OkHttp是Android平台推荐的Ktor引擎,完全支持WebSocket):

// 确保版本与Supabase SDK依赖的Ktor版本一致
implementation("io.ktor:ktor-client-okhttp-jvm:<ktor-version>")

可以通过查看项目依赖树确认Supabase使用的Ktor版本,避免版本冲突。

2. 显式指定Ktor引擎

创建SupabaseClient时,显式安装OkHttp引擎:

val supabase = createSupabaseClient(
    supabaseUrl = "https://ucqbegvfrgwmsxharvgh.supabase.co",
    supabaseKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVjcWJlZ3Zmcmd3bXN4aGFydmdoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDE1Mjc0NDMsImV4cCI6MjA1NzEwMzQ0M30.pMQ3W4I3OHO3c12QvkVxg-HMuLx0kG3Bb-uepxnjkfc"
) {
    // 先安装OkHttp引擎
    install(OkHttp)
    install(Postgrest)
    install(Auth) {
        autoSaveToStorage = true
        autoLoadFromStorage = true
    }
    install(Storage)
    install(Realtime)
    defaultLogLevel = LogLevel.DEBUG
}

3. 修正订阅逻辑的潜在问题

你的订阅代码有两处可以优化的地方:

  • 无需手动调用supabase.realtime.connect(),channel.subscribe()会自动处理连接流程
  • 避免使用GlobalScope,建议使用绑定到Android生命周期的lifecycleScope(需依赖androidx.lifecycle:lifecycle-runtime-ktx),防止内存泄漏

修正后的代码:

// 传入lifecycleScope以绑定生命周期
fun subscribeToMessages(chatroomID: String, listener: MessageListener, lifecycleScope: CoroutineScope) {
    lifecycleScope.launch(Dispatchers.IO) {
        try {
            println("trying to connect")
            val channel = supabase.channel("messages")

            val changeFlow = channel.postgresChangeFlow<PostgresAction.Insert>(schema = "public") {
                table = "messages"
                // 按聊天室ID过滤消息,避免接收无关数据
                filter = "chatroom_id=eq.$chatroomID"
            }

            changeFlow.onEach { record ->
                println("record: ${record.record}")
                // 切换到主线程更新UI
                lifecycleScope.launch(Dispatchers.Main) {
                    listener.onNewMessage(record.record)
                }
            }.launchIn(lifecycleScope)

            // 监听订阅状态
            channel.subscribe {
                onSuccess {
                    println("subscribed")
                    println("channel status: ${channel.status.value}")
                }
                onError { error ->
                    println("Subscription failed: ${error.message}")
                }
            }

        } catch (e: Exception) {
            println("Subscription Initialization Error: ${e.message}")
            e.printStackTrace()
        }
    }
}

额外检查项

  • 确认Supabase控制台中,public.messages表已开启Realtime功能
  • 检查API Key的Realtime权限配置,确保anon key有权限访问该表的实时变更

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 16:42:32