4

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.

Junior
  • 170
  • 2
  • 10
  • The concept you are looking for is called singleton class. Make repository a singleton class. For reference have a look at https://stackoverflow.com/a/51835156/10817890 – satya-p91 Feb 11 '21 at 09:14
  • I didn't find a proper way to create singleton class in `kotlin` that can also receive parameters. How can i do that? – Junior Feb 11 '21 at 09:19
  • `I need to pass parameters to the class.` this is information you need to add to your question. that's why just writing lots of text doesn't help, you need to include something others can recreate – a_local_nobody Feb 11 '21 at 09:21
  • Sorry, I am adding that to my question – Junior Feb 11 '21 at 09:23

5 Answers5

4

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

Stanislav Bondar
  • 6,056
  • 2
  • 34
  • 46
3

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)
Rajan Kali
  • 12,627
  • 3
  • 25
  • 37
  • I need to pass parameters to the class. – Junior Feb 11 '21 at 09:20
  • What do you want to pass, you can still directly variables in object as it is singleton – Rajan Kali Feb 11 '21 at 09:21
  • I need to pass a context – Junior Feb 11 '21 at 09:24
  • Ideally you can create a context variable inside `DatabaseRepository` and set it using `DatabaseRepository.context = context`, but it is never never never a recommended approach not only in this case but for any singletons because singleton variable has lifecycle of App meaning it will keep your context alive which is bad and using context in repository will couple repository with Android Framework there by hard to unit test. – Rajan Kali Feb 11 '21 at 09:30
  • What if i pass `applicationContext` instead of `activity context`? – Junior Feb 11 '21 at 09:33
  • You can do that, as it anyways stays alive through out the app lifecycle – Rajan Kali Feb 11 '21 at 09:34
  • If you modify your answer to use `applicationContext` i will accept this answer. – Junior Feb 11 '21 at 09:36
  • I will first test it and if it works then i will accept it. Thank you:) – Junior Feb 11 '21 at 09:42
2

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 }
            }
        }
    }

}


0

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

Dan Baruch
  • 1,063
  • 10
  • 19
  • 1
    I need to pass parameters to the class and i am using kotlin – Junior Feb 11 '21 at 09:20
  • I don't know Kotlin and I don't know how to convert it to Kotlin, but from what I've read around it seems rather easy, anyway, I'll update the code to give the option to provide parameters and you'll use the one that fit your needs the most. Regarding converting it, I think there's a tool in Android Studio to do so – Dan Baruch Feb 11 '21 at 10:30
0

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.

asuras
  • 123
  • 7