The key-value framework based on attribute delegation encapsulation, it doesn t suit your appetite.

The key-value framework based on attribute delegation encapsulation, it doesn t suit your appetite.

The key-value method of storage is believed to be used in every project, and the use requires packaging. The function is to operate uniformly, facilitate use and can be easily replaced if the framework is changed.

KvPref is a framework that uses kotlin attribute delegation to encapsulate. It is similar to many other similar principles. There are also many articles on the Internet, so here is mainly to see if the usage suits your liking.

Github: KvPref


1. Initialization

KvPrefModel.initKvPref( this , object : Serializer { private val gson = Gson() override fun serializeToJson (value: Any ?) : String? { return gson.toJson(value) } override fun deserializeFromJson (json: String ?, type: Type ) : Any? { return gson.fromJson(json, type); } }) Copy code

Call the initKvPref method to initialize and pass in Application. The second parameter is the serialization and deserialization interface, because KvPref supports storage of objects, and object storage is actually in the form of json strings. So serialization and deserialization are required, and the specific implementation is exposed to the business through the Serializer interface. As shown above, it is implemented with Gson. If you do not need to store the object, you do not need to pass the second parameter.

2. Create a specific key-value implementation

interface KvPrefProvider { fun get (context: Context , name: String , mode: Int ) : SharedPreferences } Copy code

There are many implementations of key-value, such as native SharedPreferences and mmkv, so here you need to implement a KvPrefProvider interface to tell the framework what your method is.

For example, I use SharedPreferences:

class DefKvPref : KvPrefProvider { override fun get (context: Context , name: String , mode: Int ) : SharedPreferences { return context.getSharedPreferences(name, mode) } } Copy code

For example, I use mmkv:

class MmKvPref : KvPrefProvider { override fun get (context: Context , name: String , mode: Int ) : SharedPreferences { if (MMKV.getRootDir().isNullOrEmpty()) { MMKV.initialize(context) } return MMKV.mmkvWithID(name, MMKV.SINGLE_PROCESS_MODE) as SharedPreferences } } Copy code

3. Create a class to store the key-value configuration

Create a class, object type, and make it inherit KvPrefModel, then the creation is completed.

object SpFileDemo: KvPrefModel( "spFileName" ) {... } Copy code

KvPrefModel has two parameters. The first parameter is the file name of the key-value file, and the second parameter is KvPrefProvider, which is the specific implementation. The file name is required, and the second parameter can be omitted. If it is not passed, the default implementation is SharedPreferences. If you use mmkv, you can do this:

object SpFileDemo: KvPrefModel( "spFileName" , MmKvPref()) {... } Copy code

4. Specific use

object SpFileDemo: KvPrefModel( "spFileName" ) { var people: People? by objPrefNullable(People().apply {age = 100 ;name = "eating dog shit" }) var otherpeople: People by objPref() var name: String by stringPref() var otherName: String? by stringPrefNullable() var age: Int by intPref() var height: Long by longPref() var weight: Float byfloatPref() var isGay: Boolean ? by booleanPrefNullable( false , key = "Is it abnormal" ) } = "Aunt egg" Log.i ( "the MainActivity" , "Read =" + copying the code

As above, some values are defined in SpFileDemo. When using, assigning a value is writing key-value, and getting the value directly is reading key-value.

The type of each value corresponds to this corresponding xxxPref() method, and the type of value corresponds to the specific type of reading and writing key-value. For example, stringPref corresponds to putString and getString.

Each xxxPref() method has two kinds, one is xxxPref() and the other is xxxPrefNullable, because kotlin checks for null is strict, so if the value you use may be null, please use the xxxPrefNullable method, the others are no different . The object corresponds to objPref() and objPrefNullable()

5. xxxPref() method

fun stringPref ( default: String = "" , key: String ? = null , keyUpperCase: Boolean = isKeyUpperCase, synchronous: Boolean = isCommitProperties ) Copy code

Let's take a look at what the xxxPref() method can do. Here we use stringPref as an example. The other methods are the same.

First of all, each xxxPref() method has 4 parameters, default stands for the default value, so I won t talk about it, everyone knows. The key represents the stored key, and it is empty by default. When it is empty, the storage is the real key and the variable name is taken. When it is not empty, the key is taken. keyUpperCase represents whether to change the key to uppercase, the default is false. synchronous represents whether to use apply() or commit(), false represents to use apply(), the default is false.

6. Compatible with Java usage

object SpFileDemoJava { fun setPeople (people: People ) { SpFileDemo.people = people } fun getPeople () = SpFileDemo.people } Copy code

Because attribute delegation cannot be used directly in Java code, it can only be a little troublesome and wrap it up again, and it's better to...

7. Batch operation

If you want to operate N key-values at the same time, you need to do batch operations, because it is not good to do them one by one. There are 4 APIs related to batch operations:

fun beginBulkEdit () //Start batch operation fun applyBulkEdit () //Apply form ends batch operation fun commitBulkEdit () //commit form ends batch operation fun cancelBulkEdit () //Release resources copy code

Usage example:

SpFileDemo.beginBulkEdit() = "Xiao Ming" SpFileDemo.age = 18 SpFileDemo.isGay = true SpFileDemo.applyBulkEdit() SpFileDemo.cancelBulkEdit() Copy code

You can see the code comparison template, so here is also an extension function to use directly:

SpFileDemo.applyBulk { name = " " age = 18 isGay = true } Copy code

applyBulk calls apply(), of course you can also use commitBulk

8. Dynamic key storage function

In actual projects, we often encounter such a situation. The key that needs to be stored is dynamic. What does it mean?

For example, there is a color configuration that needs to follow the user, and different users are different, so it is likely to do this when storing: color_config_312312

The color_config_ is a part with a fixed key, and the following series of numbers are the userIds of different users. In this way, each userId corresponds to a key, so color_config is not fixed, but dynamic.

Now let's see how to use KvPref to accomplish this requirement. First we need to define a variable as the fixed part of the key:

object SpFileDemo: KvPrefModel( "spFileName" ) { var colorConfig: String? = null } Copy code

Because property delegation cannot change the property name of the property, property delegation cannot be used to change the requirement, so after defining the variable, there is no need to use by or something. Instead , you can define it at will . Variable types and assignments can be arbitrary, because only the variable name colorConfig is used here.

Next, we use the two extension methods saveWithKey and getWithKey of KvPrefModel to complete the access operation:

SpFileDemo.saveWithKey (SpFileDemo :: colorConfig, "312 312" , "#FFFFFF" ) Val Color = SpFileDemo.getWithKey <String> (SpFileDemo :: colorConfig, "312 312" ) copy the code

As above, when using saveWithKey, you need to pass in 3 parameters, the first is the key of the fixed part, which is obtained by ::, the second parameter is the key of the dynamic part, which is equivalent to the userId mentioned above, and the third is The specific value. When storing, there is no need to specify a specific storage type. If the third parameter is a String, it will be automatically recognized as a String type. If it is a one-inch number, it will be recognized as an Int. The above saveWithKey will have the following results in the sp file:

< int name = "colorConfig_312312" value = "#FFFFFF"/> Copy code

When using getWithKey, you only need to pass in a fixed + dynamic key. However, you need to pass in the acquired data type when acquiring, otherwise a type conversion error will occur.

In terms of other functions, as in other cases, for example, saveWithKey can also be called in batch operations:

SpFileDemo.applyBulk { saveWithKey(SpFileDemo::haha, "13" , "Open when punching in" ) saveWithKey(SpFileDemo::haha1, "24" , "dasjd" ) saveWithKey(SpFileDemo::haha2, "13" , "Sadaanka" ) name = "How long does Dallas leave?" } Copy code

9. Data Migration

KvPref provides a data migration method and supports other key-value implementations that implement the SharedPreferences interface to migrate data to KvPref. For example:

class KvMigrateProvider : ContentProvider () { override fun onCreate () : Boolean { if (context != null && context is Application) { KvPrefModel.initKvPref(context as Application, object : Serializer { private val gson = Gson() override fun serializeToJson (value: Any ?) : String? { return gson.toJson(value) } override fun deserializeFromJson (json: String ?, type: Type ) : Any? { return gson.fromJson(json, type); } }) SpFileDemo.migrate(PreferenceManager.getDefaultSharedPreferences(context)) } return true } //...Copy code

In order to execute the migration logic as soon as possible, the ContentProvider is used here, and then the initialization is also placed in it by the way, and the data migration is completed through the migrate method.

10. LiveData form monitoring SharedPreferences.OnSharedPreferenceChangeListener

If you want to listen to the OnSharedPreferenceChangeListener of a field, you can do this:

SpFileDemo.asLiveData(SpFileDemo::name).observe( this , Observer { //... }) Copy code

11. Other API

Fun the Remove (Property: KProperty <*>, Synchronous: Boolean = isCommitProperties) Fun getPrefKey (Property: KProperty <*>) : String? Fun getPrefName (Property: KProperty <*>) :? String Fun getAll () Copy the code

Remove means to delete, getPrefKey is to get the key value of key-value, getPrefName is to get the variable name, and getAll is to get all the data.

How to use:

SpFileDemo.remove (SpFileDemo :: name) Val prefKey = SpFileDemo.getPrefKey (SpFileDemo :: name) Val prefName = SpFileDemo.getPrefName (SpFileDemo :: name) Val Map = SpFileDemo.getAll () to copy the code

Note that the parameters are passed in in the form of double colons