Does Kotlin call a method call method

To increase the readability of calls in SharedPreferences.Editor, I want to use the Kotlin variable, which will execute 'getSharedPreferences.edit ()' every time I need a new SharedPreferences.Editor. I was originally going to use something like this:

val editPreferences: SharedPreferences.Editor = Application.getSharedPreferences("preferences", Context.MODE_PRIVATE).edit() 

But then I was informed that "editPreferences" would contain a link to the same editor when I really want it to create a new editor every time "editPreferences" is called.

If a custom getter was used, will the new editor come back every time? Something like that:

 val editPreferences: SharedPreferences.Editor get() = Application.getSharedPreferences("preferences", Context.MODE_PRIVATE).edit() 

Still getting up and working with Kotlin, and I'm not sure if the get () method will reference the editor instead of creating a new one.

+7
android kotlin
source share
4 answers

The second declaration of the property meets your needs: it has a custom getter , so getting the value of the property will always be done by the getter, and the value will not be saved (the property has no support field ).

You are probably confused by the equal sign in get() = ... , but this is just a shorthand for one expression for the equivalent form of getter:

 val editPreferences: SharedPreferences.Editor get() { return Application .getSharedPreferences("preferences", Context.MODE_PRIVATE) .edit() } 
+3
source share

If you implement a property with a custom recipient, it will not store any data. The body of the recipient will be executed each time the property is accessed.

+6
source share

You can take another step and wrap the property with a delegate using the variable name in the metadata.

Using

 class SomeActivity : SomeBaseActivity { // Declare property the with key "myImportantNumber" // and default value 10 var myImportantNumber by preference(10) //how to access the property fun isMyImportantNumberReallyHight() = myImportantNumber > 100 //how to edit the property fun incrementMyImportantNumber(times:Int){ myImportantNumber = myImportantNumber + times } } 

Delegation

A delegate stores an instance of some preference manager and uses metadata in the property to retrieve and store values ​​in general preferences.

 class DelegatedPreference<T>(val default: T, val contextProvider:()-> Context) { val manager by lazy { PreferencesManager(contextProvider()) } @Suppress("UNCHECKED_CAST") operator fun getValue(thisRef: Any?, prop: KProperty<*>): T { return manager.get(prop.name, default) } operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: Any) { manager.save(prop.name, value) } class TypeNotImplementedException(val propName:String) : Exception("Type of ${propName} is not implemented on DelegatedPreference and thus invalid") } 

Some sugar

A small extension method:

 fun <T> Activity.preference(default:T):DelegatedPreference<T>{ return DelegatedPreference(default, {this}) } 

This allows us to change this:

 var myImportantNumber by DelegatedPreference(10, {this}) 

Something more readable:

 var myImportantNumber by preference(10) 

Actual retrieval and retention

Here, what I called the PreferencesManager (sorry, I did not come up with a better name) does the hard work and calls .edit() every time the property needs to be changed. It would look like this:

 public class PreferencesManager(context: Context) { private val preferences = getSharedPreferences(context) companion object Utils { public val APP_PREFERENCES: String = "APP_PREFERENCES" fun getSharedPreferences(context: Context): SharedPreferences { return context.getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE) } } public fun save(label:String, elem:Any){ when(elem){ is Int -> preferences.edit().putInt(label, elem).apply() is String -> preferences.edit().putString(label, elem).apply() is Float -> preferences.edit().putFloat(label, elem).apply() is Boolean -> preferences.edit().putBoolean(label, elem).apply() else -> throw DelegatedPreference.TypeNotImplementedException(label) } } @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY") public fun <T> get(label:String, default:T):T = when(default){ is Int -> preferences.getInt(label, default) is String -> preferences.getString(label, default) is Float -> preferences.getFloat(label, default) is Boolean -> preferences.getBoolean(label, default) else -> throw DelegatedPreference.TypeNotImplementedException(label) } as T } 

There are many opportunities for improvement (for example, parameterizing the preference name instead of hard coding, providing an extension point for serializing other types, etc.), but the general idea remains.

+5
source share

As the hotkey and yole are indicated, the custom getter will each time return a new editor what you want.

However, please consider this extraordinary solution that uses:

  • inlining to reduce overhead when calling a function
  • extions so that it looks like a method of the Context class, although we did not define it
  • higher order functions to subsequently get rid of the commit() call when using it

Declaration

 inline fun Context.editPreferences(preferenceFileName:String = "preferences",block:SharedPreferences.Editor.() -> Unit) { val editablePreferences = getSharedPreferences(preferenceFileName,Context.MODE_PRIVATE).edit() editablePreferences.block() editablePreferences.commit() } 

Using

 Application.editPreferences() { putBoolean("SOME_BOOLEAN",true) putFloat("SOME_FLOAT",293232F) } 

or in many cases when the receiver is already Context , you can do this:

 editPreferences() { putBoolean("SOME_BOOLEAN",true) putFloat("SOME_FLOAT",293232F) } 

Kotlin ❀

+3
source share

All Articles