Search code examples
androidkotlinandroid-syncadapterandroid-room

Singleton Room database in SyncAdapter to trigger LiveData


I'm struggling to use Room as singleton in my SyncAdapter. I use Kotlin.

My room class

@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()
    }

}

I retrieve database instance like this

val db = AppDatabase.getInstance(applicationContext)

And my issue is that I always get two different instances of AppDatabase in my activities and SyncAdapter. Though among activities AppDatabase object is indeed singleton .AppDatabase_Impl@3c9ff34d, and for each onPerformSync() the AppDatabase is singleton too .AppDatabase_Impl@7d7718d. But as you can see they are two different objects. Can any one explain what I miss here?

On the other hand maybe I'm conceptually wrong in what I'm trying to achieve. Then any advice would be appreciated.

The point is to use LiveData to update UI components when new data is inserted from remote server via SynAdapter. In this case I have to use the same productDao object in ViewModel/Activity and in SyncAdapter in order to trigger LiveData on inserting new products, otherwise it won't be triggered. So, to obtain the same productDao I must get the same(i.e. singleton) AppDatabase.

I know that this can be implemented using a ContentProvider, that is triggered automatically on inserting new data. But I really want to try new android architecture components. Or maybe using ContentProvider is the only correct way to implement this use-case?


Solution

  • When you want an easy communication between different Threads you need to be in the same process.

    In the AndroidManifest.xml you can specify an attribute called android:process=":processName" usable on Activities, Services(which relates to SyncAdapters), Content Providers and Broadcast Receivers, which can help you exceed the default limits of Heap (memory) for single process.

    Those are the summarized PRO/CONS:

    Multi-process PRO: You have more memory to run your app and if a process crashes it doesn't crash the other ones.

    Multi-process CONS: It's a lot more difficult (but not impossible) to let the processes communicate with each other but obviously you can't share the state between them (in your case the state is the singleton)

    For a deep understanding, you should read this article and this good answer

    My suggestion: If your app is not so complex and you don't have memory problems I'll suggest you start with the single process approach. While if you want to decouple the SyncAdapter which update your data from the app, or at some point in the development you find bottlenecks or crashes related to the single-process, you could switch to multi-process, remove direct LiveData and use ContentProvider to communicate data changes.