【问题标题】:Singleton Room database in SyncAdapter to trigger LiveDataSyncAdapter 中的 Singleton Room 数据库触发 LiveData
【发布时间】:2017-12-02 10:39:36
【问题描述】:

我正在努力将Room 用作我的SyncAdapter 中的单例。我使用 Kotlin。

我的房间课

@Database(entities = [(Product::class)], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
    abstract fun productDao(): ProductDao

    companion object {
        @Volatile private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context) : AppDatabase =
            INSTANCE ?: synchronized(this) {
                INSTANCE ?: buildDatabase(context.applicationContext)
                        .also {INSTANCE = it}
            }

        private fun buildDatabase(context: Context) =
                Room
                        .databaseBuilder(context.applicationContext,
                                AppDatabase::class.java, "database.db")
                        .allowMainThreadQueries()
                        .build()
    }

}

我这样检索数据库实例

val db = AppDatabase.getInstance(applicationContext)

而我的问题是,我在活动中总是得到两个不同的 AppDatabase 实例和 SyncAdapter。虽然在活动中AppDatabase 对象确实是单例.AppDatabase_Impl@3c9ff34d,并且对于每个 onPerformSync(),AppDatabase 也是单例.AppDatabase_Impl@7d7718d。 但正如你所看到的,它们是两个不同的对象。 谁能解释一下我在这里想念什么?

另一方面,我想要实现的目标可能在概念上是错误的。那么任何建议将不胜感激。

重点是在通过SynAdapter 从远程服务器插入新数据时使用LiveData 更新UI 组件。在这种情况下,我必须在 ViewModel/ActivitySyncAdapter 中使用相同的 productDao 对象,以便在插入新产品时触发 LiveData,否则不会触发。 因此,要获得相同的productDao,我必须获得相同的(即单例)AppDatabase

我知道这可以使用ContentProvider 来实现,它会在插入新数据时自动触发。但我真的很想尝试新的 android 架构组件。 或者也许使用ContentProvider 是实现这个用例的唯一正确方法?

【问题讨论】:

  • 你的SyncAdapter和主线程在同一个进程吗? (这是您在清单中定义的内容)
  • 不,我的 SyncAdapter 使用 android:process=":sync"。我想我明白你的意思了:进程之间不共享单例?
  • 是的,这可能是“唯一”的问题。让我们尝试删除process 行并将结果发布回此处。
  • 从清单中删除单独全局进程的定义,即android:process=":sync",起到了作用。现在我在活动和同步提供者之间拥有相同的数据库和 dao 对象。非常感谢你的帮助。但是,如果我只使用一个应用程序进程,此解决方案是否存在任何重大缺陷?
  • 我现在将发布一个完整的答案,让我解释一下;)

标签: android kotlin android-syncadapter android-room


【解决方案1】:

当您想要在不同的Threads 之间轻松通信时,您需要在同一个进程中。

AndroidManifest.xml 中,您可以指定一个名为android:process=":processName" 的属性,该属性可用于ActivityServices(与SyncAdapters 相关)、Content ProvidersBroadcast Receivers,可以帮助您超出单个进程的 Heap(内存)的默认限制。

这些是总结的优点/缺点:

多进程 PRO:您有更多内存来运行您的应用程序,如果 进程崩溃它不会使其他进程崩溃。

多进程缺点: 让流程变得更加困难(但并非不可能) 彼此通信,但显然您不能在它们之间共享状态(在您的情况下,状态是单例)

要深入了解,你应该阅读this articlethis good answer

我的建议:如果您的应用程序不是那么复杂并且您没有内存问题,我建议您从单进程方法开始。如果您想将更新数据的 SyncAdapter 与应用程序分离,或者在开发中的某个时刻您发现与单进程相关的瓶颈或崩溃,您可以切换到多进程,删除直接 LiveData 并使用 ContentProvider 来传达数据变化。

【讨论】:

  • 但它会以任何方式影响 android 关闭我的应用程序吗?我的意思是,如果不需要,android 可以杀死“繁重”的进程,即我退出了我的应用程序,android 会在一段时间内杀死它。但是如果我的 SyncAdapter 运行在 Application 所在的同一个进程中,是不是意味着这个进程永远不会被杀死,而我的应用程序总是在后台处于活动状态??
  • 无论如何我接受你的回答。你帮了我很多,谢谢。进一步讨论只是为了全面了解交易。
  • 这真的取决于你的需要。如果您想使用 SyncAdapter 中的 LiveData 功能,您必须将 SyncAdapter 放在同一进程中。如果您希望系统有更多机会不会杀死您的应用程序,您可以使用多进程方法并使用内容提供程序来访问数据。正如我所说,这完全取决于你想用你的应用程序实现什么
  • @akamuza 我已经更新了我的最终建议,希望现在更清楚了^^
  • 帮助了我很多。谢谢!如果您从 SynService 中删除“android:process=":processName",它将起作用。
【解决方案2】:

使用匕首2:

class MyApplication : Application(){

    val component: AppComponent by lazy {
        DaggerAppComponent.builder().appModule(AppModule(this)).build()
    }

    @Inject
    lateinit var database : AppDatabase

    override fun onCreate() {
        super.onCreate()
        component.inject(this)
    }

}
@Singleton
@Component(modules = arrayOf(
        AppModule::class
))
interface AppComponent {
   fun inject(app: MyApplication)
}

@Module
class AppModule constructor(private val context: Context) {

@Provides
@Singleton
fun providesDatabase() = Room.databaseBuilder(context, AppDatabase::class.java, "mydb.db").build()

}

来自活动:

@Inject lateinit var mRoomDatabase: AppDatabase

override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     (application as MyApplication).component.inject(this)
}

【讨论】:

  • 我尝试过使用 Dagger2,但它对我来说太难了,因为我是新手,主要问题是我无法在 SyncAdapter 中注入 AppDatabase。那么你能解释一下,我怎样才能注入SyncAdapter
  • 如果我理解得很好,你需要的是一个单例数据库和使用架构组件,你想监听来自 dao 类的更改并自动刷新关联的适配器,对吧?
  • @sokarcreative 他已经成功实现了单例模式,他的问题与为 SyncAdapter 使用第二个进程有关。 Dagger 并不是这个特定问题的解决方案(因为他可能不需要 Dagger 为单个 Singleton 带来的所有复杂性)
  • 是的,您理解正确。但是从 SyncAdapter 进行了更改,因此我必须在同步适配器和视图模型之间共享相同的 dao。但是同步适配器我们在一个单独的进程中运行。
  • @MatPag 好吧,我现在明白了。确实 Dagger2 没有解决它:/
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多