Closed blonsky95 closed 3 years ago
Ok so DataStore is in Alpha and it relies on coroutines and Flow - which combined aim to substitute RxJava - permits asynchronous code to work
Here about flow: https://blog.mindorks.com/what-is-flow-in-kotlin-and-how-to-use-it-in-android-project
And here about DataStore basic setup with better context https://medium.com/better-programming/jetpack-datastore-improved-data-storage-system-adec129b6e48
should probably do this to understand coroutines/livedata/flow better : https://codelabs.developers.google.com/codelabs/advanced-kotlin-coroutines/#0
And this for actual Datastore https://developer.android.com/codelabs/android-preferences-datastore#5
just watch philip lackner
https://www.youtube.com/watch?v=ShNhJ3wMpvQ&list=PLQkwcJG4YTCQcFEPuYGuv54nYai_lwil_&ab_channel=PhilippLackner for coroutines
Do the flow thing with DataStore first, then watch all the things from philipp!
Next - call the functions that called through Utils, through coroutines and a lifecycle scope - see here example https://www.youtube.com/watch?v=McnVx7l5awk&ab_channel=PhilippLackner DONE Next- test if the prefs are working - need to make the pref class work because datastore is null in most calls (init at start?) - WORK DONE Next - think how to initialise datastore correctly - DONE (missing DI) Next - clean up and document Next- write/sum up here how to use Datastore for preferences
STILL needs TESTING for all functions
First how the class works:
PreferenceDataStore is a class that requires context to be initiliased - then used to init DataStore However the PrefernceDataStore class is instantiated in every class that requires its usage, but to avoid different instances or simultaneous update/read operations, the class is like a singleton, to achieve this the following code is done:
companion object {
//Volatile means that writes to this field are immediately made visible to other threads
// - hence if there is already an instance in other thread, it would use that one
@Volatile private var INSTANCE:PreferencesDataStore? = null
//Synchronized makes sure the code inside the block cant be run from two multiple threads at the same time
//its like a room is the code, and each time a thread accesses it, it locks the room behind it,
// so until it finishes its job in the room, no other thread can access
fun getInstance(context: Context): PreferencesDataStore =
INSTANCE ?: synchronized(this) {
INSTANCE ?: createClassInstance(context).also { INSTANCE = it }
}
private fun createClassInstance(context: Context) =
PreferencesDataStore(context)
}
Like with a RoomDatabase - google architecture
The actual DataStore works as such:
initialization:
private var dataStore: DataStore<Preferences> = context.createDataStore(
name = "tracktimer_preferences"
)
reading operation:
runs from a coroutine - so its a suspend function, the preference variable contains all the key pairs, so because flow is an array of data that is streamed, and we only have one preference, we use first() and get the reference key we want. Then add the elvis operator to return the default value if preference is not existing
datastore.data returns androidx.datastore.preferences.core.Preferences so getting first of this.
suspend fun isUserSubscribed(): Boolean {
val preference = dataStore.data.first()
return preference[PREF_IS_SUBSCRIBED]?:false
}
writing operation: same thing but edit function gets mutablePreferences type from datastore, you then modify the key you want
suspend fun updateIsUserSubscribed(isSubscribed: Boolean) {
dataStore.edit { prefs ->
prefs[PREF_IS_SUBSCRIBED] = isSubscribed
}
}
And then to call from activity
lifecycleScope.launch {
mainViewModel.checkIfCanStartTiming(mainActivity)
}
or from viewmodel
viewModelScope.launch {
preferencesDataStore.updateIsTimingFreeActive(true)
}
Also using this when I am trying to update something from a coroutine that I dont want binded to a lifecycle, for example when I finish in onBoardingActivity it sends an intent to open to mainActivity, but I also want to update the preference of firstTimer, so I run the coroutine to update the value on a globalscope coroutie, that wont die when the OnboardingActivity finishes shortly after the intent. Otherwise use the lifecyclescope or viewmodelscope - this more coroutines related
GlobalScope.launch {
//dont want to block ui, even if super short simple task, it should be done in bg
PreferencesDataStore.updateUserFirstTimer(context, false)
}
Continue testing but jump to dependency injection after testing
https://developer.android.com/topic/libraries/architecture/datastore