I try to store & get data using a datastore-preference alpha07, everything working fine, I got some memory leak issue in the datastore
What's the best practice to prevent memory leaks using Datastore?
here is my sample code:
// Preferences DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha07"
// Leakcanary find memory leak
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
UserManager.kt
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class UserManager(val dataStore: DataStore<Preferences>) {
//Create some keys
companion object {
val USER_AGE_KEY = intPreferencesKey("USER_AGE")
val USER_FIRST_NAME_KEY = stringPreferencesKey("USER_FIRST_NAME")
val USER_LAST_NAME_KEY = stringPreferencesKey("USER_LAST_NAME")
val USER_GENDER_KEY = booleanPreferencesKey("USER_GENDER")
}
//Store user data
suspend fun storeUser(age: Int, fname: String, lname: String, isMale: Boolean) {
dataStore.edit {
it[USER_AGE_KEY] = age
it[USER_FIRST_NAME_KEY] = fname
it[USER_LAST_NAME_KEY] = lname
it[USER_GENDER_KEY] = isMale
}
}
//Create an age flow
val userAgeFlow: Flow<Int?> = dataStore.data.map {
it[USER_AGE_KEY]
}
//Create a fname flow
val userFirstNameFlow: Flow<String?> = dataStore.data.map {
it[USER_FIRST_NAME_KEY]
}
//Create a lname flow
val userLastNameFlow: Flow<String?> = dataStore.data.map {
it[USER_LAST_NAME_KEY]
}
//Create a gender flow
val userGenderFlow: Flow<Boolean?> = dataStore.data.map {
it[USER_GENDER_KEY]
}
}
User.kt
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
// At the top level of your kotlin file:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_prefs")
SignInFragment.kt
//Get reference to our userManager class
var userManager = UserManager(requireContext().dataStore)
//Stores the values
lifecycleScope.launch {
userManager.storeUser(1, "android", "studio", true)
}
HomeFragment.kt
var userManager = UserManager(requireContext().dataStore)
userManager.userAgeFlow.asLiveData().observe(requireActivity(), {
if (it != null) {
age = it
//tv_age.text = it.toString()
}
})
Memory leak issue:
Below solution not working I tried, still I am getting leaks even tried this solution, Observer should be bind with component's lifecycle in this case Fragment's lifecycle . Use viewLifeCycleOwner instead to bind the observer.
HomeFragment.kt
var userManager = UserManager(viewLifecycleOwner.dataStore)
userManager.userAgeFlow.asLiveData().observe(viewLifeCycleOwner, {
if (it != null) {
// age = it
// tv_age.text = it.toString()
}
})
Ref screenshot (again replicated)
Using your Activity's
or Fragment's
context is indeed fishy, and actually there's no need to do so. Instead, if you pass a global context of your application (applicationContext
), then DataStore
won't leak any UI contexts and will process your Preferences
requests independently from the lifecycle of the UI component on which they start.
In your Fragments
, write the following:
...
var userManager = UserManager(requireContext().applicationContext.dataStore)
...
In your MainActivity
, it will be even simpler:
...
var userManager = UserManager(applicationContext.dataStore)
...
That's how many leaks have been detected:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With