I am using a custom DataStore which inherits PreferenceDataStore to store preferences for settings activity in my android application. I want to implement the getString() method as the PreferenceDataStore.getString() method doesn't do anything.
-> PreferenceDataStore.getString()
/**
* Retrieve a String value from the data store.
*
* #param key The name of the preference to retrieve.
* #param defValue Value to return if this preference does not exist.
* #see #putString(String, String)
*/
#Nullable
default String getString(String key, #Nullable String defValue) {
return defValue;
}
This is my SettingsPreferences class:
class SettingsPreferences(lifecycleCoroutineScope: LifecycleCoroutineScope, context: Context) :
PreferenceDataStore() {
companion object {
private const val TAG = "SettingsPreferences"
val THEME = stringPreferencesKey("theme")
}
private val scope = lifecycleCoroutineScope
private val currentContext = context
override fun putString(key: String?, value: String?) {
scope.launch {
currentContext.dataStore.edit {
it[stringPreferencesKey(key.toString())] = value ?: ""
}
}
}
override fun getString(key: String?, defValue: String?): String? {
//TODO implement getString
return super.getString(key, defValue)
}
fun storeTheme(theme: String) {
putString(THEME.name, theme)
}
}
I'm not sure whether using kotlin is the problem. I tried this method:
currentContext.dataStore.data.first()[stringPreferencesKey(key.toString())]
but the first() is an async method, so I have to suspend getString() or use coroutine, but getString() method which is an inherited method cannot be suspended and using coroutine, I can't return the value. What or how should I implement for this method?
Note:
There is an alternative method to update values in Preference screen in android with custom implementation, but it is hectic and my objective is to implement putString() method so that the PreferenceFragmentCompat class handles it appropriately.
A possible option is to use runblocking, but that's more of a workaround than a solution, so you can use it and come back to your task later on
Related
I want to create a helper object that helps me to access strings from any where of project. Also this helper object should work in unit tests too. But I can not be sure that if is there any risk of memory leak for example usage ?
This is the helper object.
object ResourceHelper {
private var getString: (Int) -> String = {
it.toString()
}
private var getStringWithArgs: (Int, Array<out Any>) -> String = { id, args ->
"$id${args.contentToString()}"
}
fun getString(#StringRes id: Int): String {
return getString.invoke(id)
}
fun getString(#StringRes id: Int, vararg args: Any): String {
return getStringWithArgs.invoke(id, args)
}
fun initialize(resources: Resources) {
getString = { id -> resources.getString(id) }
getStringWithArgs = { id, args -> resources.getString(id, *args) }
}
}
This is the only activity in project.
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
ResourceHelper.initialize(resources)
}
}
This is the view model class, I do not have very detailed knowledge about how references is hold in stack. Is there any problem with this approach ?
class MyViewModel : ViewModel() {
fun printString(id: Int) {
val s = ResourceHelper.getString(id)
Log.d("*****", s)
}
}
The Resources object from an Activity is different each time the Activity is started due to a configuration change.
Your code will temporarily leak the Resources object in between the times when the Activity is destroyed and the next time it is recreated (in onCreate() when you call initialize() again and overwrite which Resources object is being held by reference). If that's just the time during an orientation change, it's trivial. But if it's during the time between the user backing out of the Activity and the next time they return to the app, then your app will be hanging onto a little more memory than necessary.
State-holding singletons like this make unit testing difficult, anyway. I suggest creating extension functions as an alternative. If you need String resources access in a ViewModel, you can extend from AndroidViewModel, which has Resources access through the Application.
fun AndroidViewModel.getString(#StringRes id: Int) =
getApplication<Application>().resources.getString(id)
fun AndroidViewModel.getString(#StringRes id: Int, vararg args: Any) =
getApplication<Application>().resources.getString(id, args)
I want to convert some 3rd-party API based on callbacks to simple suspend functions that are easy to use. Here's a simplified example of mine implementation.
class LibraryWrapper {
private lateinit var onFooFired: (Boolean) -> Unit
private val libraryCallback = object : LibraryCallback {
override fun foo(result: Boolean) {
onFooFired(result)
}
}
private val library = Library(libraryCallback)
suspend fun bar(): Boolean {
return suspendCoroutine { performingBar: Continuation<Boolean> ->
onFooFired = {fooResult -> performingBar.resume(fooResult) }
library.bar()
}
}
}
But this solution sometimes works and sometimes not. There is such an issue with this lambda field, that sometimes it initializes correctly, but sometimes the exception is thrown that "lateinit property onFooFired is not initialized".
It's very strange because I do initialize it before run library.bar() and foo of LibraryCallback is called only after library.bar() was called.
first of all, I think it is not a good approach to use "lateinit var" when you don't control the initialization of a field. Use lateinit only when you have the promise of initialization.
Try to use nullable field implementation like
private var onFooFired: ((Boolean) -> Unit)? = null
and in callback :
private val libraryCallback = object : LibraryCallback {
override fun foo(result: Boolean) {
onFooFired?.invoke(result)
}
}
In this case, until you do not have an implementation of "onFooFired" lambda, it does not invoke
I have a class below that updates a data variable. How can I observe when this variable changes?
object Manager {
private var data: Type = B()
fun doWork{
while(active) {
if(conditionA)
data = A()
else if(conditionB)
data = B()
}
}
fun getData(): Flow<Type>
}
interface Type {
}
Some classes that implements the interface.
class A: Type {}
class B: Type {}
I want to be able to observe these changes without using LiveData or anything that is Experimental. How can I let other areas of my code observe the data variable?
I know there is BroadcastChannel but I cannot use it because it is experimental.
You can use listener and built-in Kotlin delegate:
object Manager {
var dataListeners = ArrayList<(Type) -> Unit>()
// fires off every time value of the property changes
private var data: Type by Delegates.observable(B()) { property, oldValue, newValue ->
dataListeners.forEach {
it(newValue)
}
}
fun doWork{
while(active) {
if(conditionA)
data = A()
else if(conditionB)
data = B()
}
}
}
I'm not sure what I'm looking to do is even possible. If it is, it's new to me.
here's a basic outline of what I'm trying to accomplish:
class MyClass : SomeInterface {
fun makeSomethingHappen() {
methodInInterfaceThatReturnsValueBelow()
}
override fun iDidSomething(result: Value) {
//give this value back to the original caller of makeSomethingHappen()
}
override fun iDidSomethingElse(result: Value) {
//give this value back to the original caller of makeSomethingHappen()
}
override fun onFailure(result: Value) {
//give this value back to the original caller of makeSomethingHappen()
}
}
Explanation:
We're using a required SDK that has about 15 overrides. I call into this class to call a function in the SDK. That function is going to call one of the override functions when it's done.
Is there a way (live data, flows, anything) to have whoever called makeSomethingHappen() receive the value from any of the override methods?
This is a basic example of reactive programming. You wait for an event that a producer/observable emits, it's like when you declare a click listener on a button.
You can't return the value in makeSomethingHappen(), but you can create a listener and the observer implements that listener to get the value.
typealias MyListener = (Value) -> Unit
class MyClass : SomeInterface {
private var _listeners: List<MyListener> = mutableListOf()
fun addListener(listener: MyListener) {
_listeners.add(listener)
}
fun makeSomethingHappen() {
methodInInterfaceThatReturnsValueBelow()
}
override fun iDidSomething(result: Value) {
// Send result Value to the listener implementations
_listeners.forEach { it.invoke(result) }
}
override fun iDidSomethingElse(result: Value) {
// Send result Value to the listener implementations
_listeners.forEach { it.invoke(result) }
}
override fun onFailure(result: Value) {
// Send result Value to the listener implementations
_listeners.forEach { it.invoke(result) }
}
}
The you can get the result implementing MyListener
val myClass = MyClass()
myClass.makeSomethingHappen { value ->
// Here you have the value and you can do whatever
print(value)
}
Kotlin has delegated properties which is a very nice feature. But I am figuring out how to get and set the values. Let's say I want to get value of the property which is delegated. In a get() method how i can access the value?
Here's an example of how I have implemented:
class Example() {
var p: String by DelegateExample()
}
class DelegateExample {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "${property.name} "
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("${value.trim()} '${property.name.toUpperCase()} '")
}
}
fun delegate(): String {
val e = Example()
e.p = "NEW"
return e.p
}
The main question I am unable to understand is, How can I set the value to the actual property on which the delegation class is assigned. When I assign "NEW" to property p, how can I store that value to the variable p or read that new value passed on to p with get? Am I missing something basic here? Any help will be much appreciated. Thanks in advance.
Just create property in delegate which will hold the value
class DelegateExample {
private var value: String? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return value ?: throw IllegalStateException("Initalize me!")
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
this.value = value
}
}
To clarify - delegates aren't values holder, they are handlers of get/set operations. You can take a look how it works under the hood if you decompile your Example class (Tools -> Kotlin -> Show Kotlin bytecode -> Decompile).
public final class Example {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = ...
#NotNull
private final DelegateExample p$delegate = new DelegateExample();
#NotNull
public final String getP() {
return (String)this.p$delegate.getValue(this, $$delegatedProperties[0]);
}
public final void setP(#NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.p$delegate.setValue(this, $$delegatedProperties[0], var1);
}
}
No magic here, just creating instance of the DelegateExample and its get/set method invoking