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

如何实例化已连接的PowerSyncDatabase?解决同步块挂起函数报错

问题描述

我尝试创建一个单例来返回PowerSyncDatabase的实例,代码如下:

class OnlineSyncDatabase {
    companion object {
//        @Volatile
        private val instance: PowerSyncDatabase? = null
        val dotenv = dotenv()

        suspend fun getDatabase(applicationContext: Context): PowerSyncDatabase {
            return instance?: synchronized(this) {
                val driverFactory = DatabaseDriverFactory(applicationContext)
                val supabaseClient = createSupabaseClient(
                    supabaseUrl = dotenv["SUPABASE_URL"],
                    supabaseKey = dotenv["SUPABASE_KEY"]
                ) {
                    install(Postgrest)
                    install(Auth)
                }

                val powerSyncDatabase = PowerSyncDatabase(
                    factory = driverFactory,
                    schema = AppSchema,
                    dbFilename = "powersync.db"
                )
                    
                powerSyncDatabase.connect(SupabaseConnector(
                    supabaseClient,
                    dotenv["POWERSYNC_URL"]
                ))
                
                powerSyncDatabase
            }
        }
    }
}

但编译器针对挂起函数connect(SupabaseConnector...)抛出错误:

The 'connect' suspension point is inside a critical section.

我不知道该把这个函数放在哪里,请问如何实例化一个已连接的PowerSyncDatabase实例?


解决方案

问题核心是:synchronized是阻塞式同步锁,而挂起函数会释放当前线程,这会破坏同步逻辑,因此编译器禁止在synchronized块内调用挂起函数。以下是两种可行的修复方案:

方案一:分离实例创建与连接逻辑

先在同步块内创建未连接的实例并缓存,再在同步块外调用挂起函数完成连接。注意要给instance添加@Volatile注解,保证多线程下的可见性:

class OnlineSyncDatabase {
    companion object {
        @Volatile
        private var instance: PowerSyncDatabase? = null
        @Volatile
        private var supabaseClient: SupabaseClient? = null
        private val dotenv = dotenv()

        // 缓存Supabase客户端避免重复创建
        private fun getSupabaseClient(): SupabaseClient {
            return supabaseClient ?: synchronized(this) {
                supabaseClient ?: createSupabaseClient(
                    supabaseUrl = dotenv["SUPABASE_URL"],
                    supabaseKey = dotenv["SUPABASE_KEY"]
                ) {
                    install(Postgrest)
                    install(Auth)
                }.also { supabaseClient = it }
            }
        }

        suspend fun getDatabase(applicationContext: Context): PowerSyncDatabase {
            instance?.let { return it }

            return synchronized(this) {
                instance ?: run {
                    val driverFactory = DatabaseDriverFactory(applicationContext)
                    PowerSyncDatabase(
                        factory = driverFactory,
                        schema = AppSchema,
                        dbFilename = "powersync.db"
                    )
                }.also { instance = it }
            }.also { db ->
                // 同步块外调用挂起函数完成连接
                db.connect(SupabaseConnector(getSupabaseClient(), dotenv["POWERSYNC_URL"]))
            }
        }
    }
}

方案二:使用CoroutineMutex替代synchronized

Kotlin协程提供的CoroutineMutex是专为协程环境设计的同步工具,支持在同步逻辑中调用挂起函数,不会阻塞线程,是更贴合协程理念的方案:

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class OnlineSyncDatabase {
    companion object {
        private var instance: PowerSyncDatabase? = null
        private val mutex = Mutex()
        private val dotenv = dotenv()

        suspend fun getDatabase(applicationContext: Context): PowerSyncDatabase {
            instance?.let { return it }
            
            return mutex.withLock {
                instance ?: run {
                    val driverFactory = DatabaseDriverFactory(applicationContext)
                    val supabaseClient = createSupabaseClient(
                        supabaseUrl = dotenv["SUPABASE_URL"],
                        supabaseKey = dotenv["SUPABASE_KEY"]
                    ) {
                        install(Postgrest)
                        install(Auth)
                    }

                    val powerSyncDatabase = PowerSyncDatabase(
                        factory = driverFactory,
                        schema = AppSchema,
                        dbFilename = "powersync.db"
                    )
                    
                    // 直接在Mutex同步块内调用挂起函数
                    powerSyncDatabase.connect(SupabaseConnector(supabaseClient, dotenv["POWERSYNC_URL"]))
                    
                    instance = powerSyncDatabase
                    powerSyncDatabase
                }
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 14:44:51