如何实例化已连接的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
相关产品推荐
相关产品推荐

