Android: How to create only one instance of DatabaseRepository.kt and use it in different activities?

Viewed 1789

EDIT: I NEED TO PASS A CONTEXT AS A PARAMETER TO THE CLASS

(DataStore and repository is the same class, don't get confused)

I have two activities, A and B and one repository. Activity A opens activity B and activity B saves data in the repository(DataStore a part of android jetpack).

I am using LiveData in both the activities to observe data change in the DataStore.

After updating new values in the DataStore from activity B, the LiveData in the activity B gets new updated values as expected. But when i return back to activity A the LiveData gets the old the data (expecting the new updated data).

I realized that it is happening because i am creating two instances of the repository in both the activities.

How can i create only one instance of the repository class and use it in both the activities? If there's better way to do it then that solution is also welcomed.

5 Answers

Single instance based on static reference and passing Context as class parameter causes memory leak. In other hand you can use Application class to create one instance of required class

class YourApp : Application(){
    val repository by lazy { YourRepository(this) }
}

and access it elsewhere with (context.applicationContext as YourApp).repository. And don't forget to declare android:name=... for application at manifest

While traditional singleton pattern like @DanBaruch mentioned works, but Kotlin has awesome keyword called object by which you can create singleton instance through out the app.

object DatabaseRepository{
    private lateinit var context: Context

    fun setAppContext(context: Context){
        this.context = context
    }
}

To set the context from Application class do below,

DatabaseRepository.setAppContext(this)

In kotlin, there are 2 ways tto create a Singleton.

  1. Use an object keyword - With this, we can have any arguments in the constructor.

  2. Or we can create a singleton class, like this:


class DbRepository () {

    companion object {
        private var instance: DbRepository? = null

        fun getInstance(context: Context): DbRepository {
            return instance ?: synchronized(this) {
                instance ?: DbRepository().also { instance = it }
            }
        }
    }

}


Can you use Singleton design pattern? If so:

class DataSource private constructor(private var mP1: Any?, private var mP2: Any?) {

fun setParam1(p1: Any) {
    mP1 = p1
}

fun setParam2(p2: Any) {
    mP2 = p2
}

companion object {
    private var mInstance: DataSource? = null
    fun getInstance(p1: Any, p2: Any): DataSource {
        if (mInstance == null)
            mInstance = DataSource(p1, p2)
        return mInstance
    }
}
}

I wrote this mockup in Android Studio and converted it to kotlin, I have no clue if it's valid code in Kotlin.

There are 2 options, either you send parameters to getInstance at the very first time you call that function and afterwards you call it with nulls as in:

//First time calling:
DataSource mMyInstance = DataSource.getInstance(param1, param2);
 //Second time calling:
 DataSource mMyInstance2 = DataSource.getInstance(null,null);

Or use setting functions and keep your getInstance with no params. Do note that choosing option 1 makes you call getInstance with useless params but makes sure you don't forget to set any needed parameter while choosing option 2 maybe have a cleaner "getInstance" call but will force you to always set the parameters which, in my opinion, is the worse option of the 2

Since the object keyword does not allow parameters, I would suggest to use dependency injection like Dagger/Hilt to create a singleton and inject it to your viewmodels.

The official documentation is worth reading to get started.

If you have no experience with dependency injection frameworks yet, you should read about the topic first and then decide if you want to integrate DI into your apps code.

Related