基于属性委托的 key-value 方式存储封装

Overview

KvPref

基于属性委托的 key-value 方式存储封装

使用

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

dependencies {
     implementation 'com.github.EspoirX:KvPref:v1.2'
}

key-value 方式的存储相信每个项目都会用,而使用需要包装,作用是统一操作,方便使用并且如果换框架什么的时候可以轻松替换。

KvPref 是利用 kotlin 属性委托封装的一个框架,跟其他很多类似的原理差不多,网上也有很多文章,所以这里主要是看看用法合不合你心意。

用法

1. 初始化

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);
    }
})

调用 initKvPref 方法初始化,传入 Application,第二个参数是序列化和反序列化的接口,因为 KvPref 是支持存储对象的,而对象存储其实是以 json 字符串形式进行。所以需要 序列化和反序列化,而具体实现通过 Serializer 接口暴露给业务,如上所示是用 Gson 实现,如果你没有存储对象的需求,则可以不传第二个参数。

2. 创建具体的 key-value 实现

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

key-value 的实现有很多种,比如原生的 SharedPreferences 和 mmkv 等,所以这里需要实现一个 KvPrefProvider 接口,告诉框架你的方式是什么。

比如我用 SharedPreferences:

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

比如我用 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
    }
}

3. 创建存放 key-value 配置的类

创建一个类,object 类型,使其继承 KvPrefModel,则完成创建。

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

KvPrefModel 有两个参数,第一个参数是 key-value 文件的文件名,第二个参数是 KvPrefProvider,即具体实现。文件名是必需要有的, 而第二个参数可以不传,不传的话默认实现就是 SharedPreferences,如果你用的是 mmkv 你可以这样:

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

4. 具体使用

object SpFileDemo : KvPrefModel("spFileName") {
    var people: People? by objPrefNullable(People().apply { age = 100;name = "吃狗屎" })
    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 by floatPref()
    var isGay: Boolean? by booleanPrefNullable(false, key = "是否是变态")
}

SpFileDemo.name = "大妈蛋"
Log.i("MainActivity", "read = " + SpFileDemo.name)

如上,在 SpFileDemo 里面定义了一些值,在使用的时候,给值赋值就是在写 key-value,直接获取值,就是在读 key-value。

每个值的类型对应这一个相应的 xxxPref() 方法,值的类型对应的就是读写 key-value 的具体类型,比如 stringPref 就是对应 putString 和 getString。

每个 xxxPref() 方法都有两种,一个是 xxxPref(),一个是 xxxPrefNullable,因为 kotlin 对 null 的检查是严格的,所以如果你 使用的值可能是 null 的话,请用 xxxPrefNullable 方法,其他没区别。对象对应的是 objPref() 和 objPrefNullable()

5. xxxPref() 方法

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

下面看看 xxxPref() 方法可以做什么,这里用 stringPref 举例,其他方法一样。

首先每个 xxxPref() 方法都有 4 个参数,default 代表是默认值,意思就不说了,大家都知道。 key 代表是存储的 key,默认为空,在空的时候,存储是真正的 key 取的是变量名,不为空的时候取的就是这个 key。 keyUpperCase 代表是否把 key 变成大写,默认 false。 synchronous 代表是使用 apply() 还是 commit(),false 代表是使用 apply(),默认 false。

6. 兼容 Java 的用法

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

    fun getPeople() = SpFileDemo.people
}

因为属性委托不能直接用在 Java 代码上,所以只能麻烦一点再包装一层,也还好把...

7. 批量操作

如果要同时操作 N 个 key-value,就需要批量操作,因为一个个来显得不好。批量操作相关的 API 有 4 个:

fun beginBulkEdit()   //开始批量操作
fun applyBulkEdit()   //apply 形式结束批量操作
fun commitBulkEdit()  //commit 形式结束批量操作
fun cancelBulkEdit()  //释放资源

用法举例:

SpFileDemo.beginBulkEdit()
SpFileDemo.name = "小明"
SpFileDemo.age = 18
SpFileDemo.isGay = true
SpFileDemo.applyBulkEdit()
SpFileDemo.cancelBulkEdit()

可以看到代码比较模版,所以这里也提供了扩展函数去直接使用:

SpFileDemo.applyBulk {
    name = "小明"
    age = 18
    isGay = true
}

applyBulk 是调用 apply() 的,当然你也可以用 commitBulk

8. 动态key存储功能

在实际项目中,经常也会遇到这样的一种情况,需求存储的 key 是动态的,什么意思?

举个例子,有一个颜色配置需要跟随用户,不同的用户不一样,所以在存储的时候很可能会这样做:color_config_312312

其中 color_config_ 是一个 key 固定的部分,而后面那一串数字是不同用户的 userId。这样每个 userId 都会对应一个 key,所以 color_config 不是固定的,而是动态的。

现在看看使用 KvPref 是如何完成这种需求的。首先我们需要定义一个变量作为 key 的固定部分:

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

因为属性委托不能改变属性的属性名,所以该需求不能使用属性委托,所以定义完变量后不需要使用 by 什么的。而是随意定义,变量类型和赋值都可以随便,因为这里用到的只是 变量名 colorConfig 而已。

接下来,我们使用 KvPrefModel 的两个扩展方法 saveWithKey 和 getWithKey 去完成存取操作:

 SpFileDemo.saveWithKey(SpFileDemo::colorConfig, "312312", "#FFFFFF")
 val color = SpFileDemo.getWithKey<String>(SpFileDemo::colorConfig, "312312")

如上,使用 saveWithKey 时,需要传入 3 个参数,第一个是固定部分的 key,通过 :: 去获取,第二个参数是动态部分的 key,相当于上面所说的 userId, 第三个就是具体的值。 而存储的时候不需要指定具体的存储类型,如果第三个参数存的是 String,会自动识别为 String 类型,如果存的是一寸数字,会识别为 Int。 上面 saveWithKey 在 sp 文件会已下面的结果出现:

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

在使用 getWithKey 时,同样的只需要传入固定+动态的 key 即可。不过在获取时需要传入获取的数据类型,不然会发生类型转换错误。

在其他功能方面,跟其他情况一样,比如同样可以在批量操作中调用 saveWithKey:

 SpFileDemo.applyBulk {
    saveWithKey(SpFileDemo::haha, "13", "打卡时打开")
    saveWithKey(SpFileDemo::haha1, "24", "dasjd")
    saveWithKey(SpFileDemo::haha2, "13", "萨达安卡")
    name = "达拉斯多久啊离开"
}

9. 数据迁移

KvPref 提供了数据迁移方法,支持其他实现 SharedPreferences 接口的 key-value 实现把数据迁移到 KvPref。 举例:

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
    }
    //...

为了尽早执行迁移逻辑,这里使用了 ContentProvider,然后顺便把初始化也放里面,通过 migrate 方法完成数据迁移。

10. LiveData 形式监听 SharedPreferences.OnSharedPreferenceChangeListener

如果你想监听某个字段的 OnSharedPreferenceChangeListener,可以这样做:

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

11. 其他 API

fun remove(property: KProperty<*>, synchronous: Boolean = isCommitProperties)
fun getPrefKey(property: KProperty<*>): String?
fun getPrefName(property: KProperty<*>): String?
fun getAll()

remove 就是删除的意思,getPrefKey 是获取 key-value 的 key 值,getPrefName 是获取变量名,getAll 是获取全部数据。

使用方式:

SpFileDemo.remove(SpFileDemo::name)
val prefKey = SpFileDemo.getPrefKey(SpFileDemo::name)
val prefName = SpFileDemo.getPrefName(SpFileDemo::name)
val map = SpFileDemo.getAll()

注意参数都是以双冒号的形式传进去的

You might also like...
KVMapper: A Key-Value Mapper app for MacOS, created with Kotlin and Compose Desktop
KVMapper: A Key-Value Mapper app for MacOS, created with Kotlin and Compose Desktop

KVMapper is an application to convert key-value pairs from one format to another. About This app was purely written in Kotlin and compiled fo

a bitcoin key collision game for android

BitteryApp BitteryApp is an opensource bitcoin key collision game for Android. How to Build BitteryApp source code build in chromium building environm

An easy-to-use, cross-platform measurement tool that pulls data out of CD pipelines and analysis the four key metrics for you.
An easy-to-use, cross-platform measurement tool that pulls data out of CD pipelines and analysis the four key metrics for you.

Maintained by SEA team, ThoughtWorks Inc. Read this in other languages: English, 简体中文 Table of Contents About the Project Usage How to Compute Contrib

Leader key for IntelliJ-based IDE's. Now IdeaVim-friendly!

Ataman Ataman - an elected leader of the Cossack troops and settlements Ataman is an Intellij Idea plugin for using leader key for bindings (almost li

Simple authentication provider for Ktor that verifies presence of the API key in the header

Ktor API Key Authentication Provider Simple authentication provider for Ktor that verifies presence of the API key in the header. Useful if you want t

A kotlin multiplatform BLS12-381 implementation for chia key management

KBLS KBLS is a kotlin multiplatform BLS12-381 implementation built for creating cross platform chia applications. Tips are much appreciated and will d

Smoothen rx value streams for e.g. sensor data using kalman filter.
Smoothen rx value streams for e.g. sensor data using kalman filter.

KalmanRx Introduction Removes the noise from float streams using Kalman Filter. Useful to smoothen sensory data e.g.: gps location, or Accelerometer.

This library allows for easy access to a Bluetooth LE device's AdRecord and RSSI value. It offers additional functionality for iBeacons.
This library allows for easy access to a Bluetooth LE device's AdRecord and RSSI value. It offers additional functionality for iBeacons.

Bluetooth LE Library for Android This library allows for easy access to a Bluetooth LE device's Advertisement Records. It also offers: A simple runnin

A circular android ProgressBar library which extends View,  and the usage same as ProgressBar,  It has solid,line and solid_line three styles. Besides, progress value can be freely customized.
A circular android ProgressBar library which extends View, and the usage same as ProgressBar, It has solid,line and solid_line three styles. Besides, progress value can be freely customized.

CircleProgressBar 中文版文档 The CircleProgressBar extends View, It has both solid and line two styles. Besides, progress value can be freely customized. I

Custom UI control for android which is showing data as a segments and a value inside them.
Custom UI control for android which is showing data as a segments and a value inside them.

Segmented Bar View for Android Custom UI control for android which is showing data as a segments and a value inside them. Screenshots Install From rep

Automatically filled the declared non-null field is missing or null with default value.

[TOC] Moshi-kotlin-nullsafe 中文版 1. moshi-kotlin moshi-kotlin support kotlin type safe check. When parsing json, fields declared as non-null types may

A IntelliJ plugin to provide check on 'value type' which is limited to numerical constant values

ValueType A IntelliJ plugin to provide check on 'value type' which is limited to

An application for tracking the value of crypto currency
An application for tracking the value of crypto currency

CriptoApp an application for tracking the value of crypto currency API https://m

A IntelliJ plugin to provide check on 'value type' which is limited to numerical constant values

ValueDef A IntelliJ plugin to provide check on 'value type' which is limited to

NavigationComponent-SendingData - Android Navigation Component : Pass value (arguments) in Fragments
NavigationComponent-SendingData - Android Navigation Component : Pass value (arguments) in Fragments

NavigationComponent-SendingData Android Navigation Component : Pass value (argum

ConstraintSetChangesTest - Simple project showing Changes of ConstraintSet value as part of mutable state in JetpackCompose.

ConstraintSetChangesTest Simple project showing Changes of ConstraintSet value as part of mutable state in JetpackCompose. Version: implementation

TabSlider - An expanding slider widget which displays selected value
TabSlider - An expanding slider widget which displays selected value

TabSlider - An expanding slider widget which displays selected value

Helps to find your surrounding's light value (for android only) using device light sensor

react-native-ambient-light-sensor Helps to find your surrounding's light value (in lux unit) (for android only) using device light sensor Installation

Releases(v1.4)
Owner
Espoir
An android developer in GuangZhou !
Espoir
A simple NoSQL client for Android. Meant as a document store using key/value pairs and some rudimentary querying. Useful for avoiding the hassle of SQL code.

SimpleNoSQL A simple NoSQL client for Android. If you ever wanted to just save some data but didn't really want to worry about where it was going to b

Colin Miller 389 Sep 25, 2022
A simple NoSQL client for Android. Meant as a document store using key/value pairs and some rudimentary querying. Useful for avoiding the hassle of SQL code.

SimpleNoSQL A simple NoSQL client for Android. If you ever wanted to just save some data but didn't really want to worry about where it was going to b

Colin Miller 389 Sep 25, 2022
A key-value database for Android

SnappyDB SnappyDB is a key-value database for Android it's an alternative for SQLite if you want to use a NoSQL approach. It allows you to store and g

Nabil Hachicha 1.8k Dec 28, 2022
✔️ Secure, simple key-value storage for Android

Hawk 2.0 Secure, simple key-value storage for android Important Note This version has no backward compatibility with Hawk 1+ versions. If you still wa

Orhan Obut 3.9k Dec 20, 2022
Android library to easily serialize and cache your objects to disk using key/value pairs.

Deprecated This project is no longer maintained. No new issues or pull requests will be accepted. You can still use the source or fork the project to

Anup Cowkur 667 Dec 22, 2022
Reactor is key value database and is a great alternative to Shared Preferences.

Reactor Reactor is a fast and secure key-value library for Android, and has an embedded database based on the JSON structure and is a great alternativ

mr amir abbas 37 Oct 30, 2022
:blowfish: An Android & JVM key-value storage powered by Protobuf and Coroutines

PufferDB PufferDB is a ⚡ key-value storage powered by Protocol Buffers (aka Protobuf) and Coroutines. The purpose of this library is to provide an eff

Adriel Café 94 Dec 7, 2022
Expirable Disk Lru Cache is a secure(with encryption) wrapper for [DiskLruCache](https://github.com/JakeWharton/DiskLruCache) that allows expiring of key/value pairs by specifying evictionTimeSpan. It has very simple API.

ExpirableDiskLruCache ExpirableDiskLruCache is a wrapper for DiskLruCache that allows expiring of key/value pairs by specifying evictionTimeSpan. It h

Vijay Rawat 24 Oct 3, 2022
MiHawk 🦅👁️ is simple and secure 🔒 Android Library to store and retrieve pair of key-value data with encryption , internally it use jetpack DataStore Preferences 💽 to store data.

MiHawk MiHawk ?? ??️ is simple and secure ?? Android Library to store and retrieve pair of key-value data with encryption , internally it use jetpack

Nedal Hasan Ibrahem 5 Sep 3, 2022
An interactive command line interface to a transactional key value store

Transactional Key-Value Store An interactive command line interface to a transactional key value store. Commands: SET <key> <value> // store the value

Eugene 0 Jan 14, 2022