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
相关产品推荐
相关产品推荐

