如何在同步代码块中调用一个挂起函数来实例化一个已连接的PowerSyncDatabase?

移动开发 2026-07-10

我正在尝试创建一个单例,用来返回一个 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...) 抛出一个错误:

“connect” 的挂起点位于临界区内部。

不过我还不知道还能放在哪里。我该如何实例化一个已连接的 PowerSyncDatabase 实例?

解决方案

我不确定你为什么不像在前一个问题中那样使用Hilt,但如果你坚持,你当然也可以手动获取一个实例。

在这种情况下,只需将 connect() 调用移动到synchronized块之外:

private var instance: PowerSyncDatabase? = null

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

        val driverFactory = DatabaseDriverFactory(applicationContext)
        PowerSyncDatabase(
            factory = driverFactory,
            schema = AppSchema,
            dbFilename = "powersync.db",
        ).also { instance = it }
    }.apply {
        val supabaseClient = createSupabaseClient(
            supabaseUrl = dotenv["SUPABASE_URL"],
            supabaseKey = dotenv["SUPABASE_KEY"],
        ) {
            install(Postgrest)
            install(Auth)
        }

        connect(
            SupabaseConnector(
                supabaseClient = supabaseClient,
                powerSyncEndpoint = dotenv["POWERSYNC_URL"],
            )
        )
    }

现在连接是在 synchronized 块完成之后才开始,因此你不再需要在 synchronized 块内部挂起——错误信息正是提醒你这一点。

请注意,我还将检查实例是否已存在的判断移动到了 synchronized 块内。否则如果有两个线程同时调用这个函数,它们都会检测到 instance 为null,然后同时进入 synchronized 块。尽管因为该块是同步的,这两次进入会一个接一个地发生,但它们仍然会执行相同的代码,创建两个独立的PowerSyncDatabase实例。使用修正后的代码,一旦先前的调用已经初始化(并随后也已连接)的实例存在,synchronized 块将立即退出整个 getDatabase() 函数,从而维持所需的单例语义。

嗯,如果你仍在使用Hilt,做起来就像简单地应用 @Singleton 注解一样容易。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章