利用Kotlin委托实现优雅地持久化存储应用配置。
- 支持纯Kotlin项目
 - 支持Android项目
 - 支持自定义持久化实现
 - 空安全
 - 配置动态监听
 
这里可以使用注解 @Config("app_config", implCls = ...) 配置存储文件名,多个配置类可分文件存储。
安卓项目可以无需指明implCls,自动使用 SharedPreference 做存储;使用数据库需要适配,参考: 自定义持久化实现
- 定义配置类
 
//注解非必须,不加注解,会使用默认配置路径,和默认存储实现
@Config("app_config", implCls = JsonSettings::class)
object AppConfig {
    //基本类型存储
    var text: String by smartKey("a")
    //可空基础类型
    var nullableInt: Int? by smartKey(null)
    var number: Int  by smartKey(50)
    //数组
    var intArr: Array<Int> by smartKey(emptyArray())
    //实体类
    var userInfo: UserInfo? by smartKey(null, encrypt = true)
    
    //实体数组  [操作即更新]  默认空列表
    var modelList by smartKeyList<ListModel>()
    //实体集    [操作即更新]  默认空集
    var modelSet by smartKeySet<ListModel>(emptySet())
    //         [操作即更新]
    var map by smartKeyMap<String, Int>()
}
//数组实体
data class ListModel(val s: String, val a: Int)
//实体类
data class UserInfo(
        var name: String,
        var email: String,
        var age: Int
)
- 此时你可以像这样使用
 
//获取存储值
val value = AppConfig.text
val n = AppConfig.number 
//实时存储
AppConfig.text = "setValue"
AppConfig.number = 0
//存储登录用户数据
val user = UserInfo("new_user", "xx@xx.xx", 0)
AppConfig.userInfo = user
//注意修改实体中的属性无法触发持久化操作
user.name = "hello"
//需要赋值操作触发
AppConfig.userInfo = user
//操作 实时存储 无需显式赋值;需要注意避免循环 add, 可以使用addAll
AppConfig.modelList.add(ListModel("string", 1))
//set map 操作同 List 实时存储
- 配置类附加功能
 
继承BaseConfig拥有配置类基础操作
object AppConfig : BaseConfig {
    //...
}// 清空此配置所有key
AppConfig.clear()
// 直接存储key
AppConfig["key"] = 1 //key, value
AppConfig["text"] = "abc" //key, value
// 加密储存(目前只支持String及实体类型加密)
AppConfig["key", true] = "value"
"key" in AppConfig // is contains key
AppConfig -= "key" // remove key
// 获取可空类型的值
val strNullable: String? = AppConfig["dont_exists_key"]
//提供默认值 以获取不可空值
val s :String = AppConfig["text", "default"] //key, default
// 获取解密后的数据
val value :String? = AppConfig["key", true]
//获取可空数据
val user :UserInfo? = AppConfig["userInfo"]
// or
val user = AppConfig.get<UserInfo?>("userInfo")
//获取加密内容
val user: UserInfo? = AppConfig["userInfo", true]
见app目录
- 你可以指定变量对应存储的key:
 
object AppConfig2 : BaseConfig {    //指定key 
    //import cn.vove7.smartkey.smartKey
    var text: String by smartKey("defaultValue", key = "your_key")
    var textWithKey: String by smartKey("aaa", key="text_key")
    //安卓项目可通过resId指定keyId
    //import cn.vove7.smartkey.android.smartKey
    var textAndroid: String by smartKey("defaultValue", keyId = R.string.key)
}- key 动态绑定
 
print(AppConfig2.textWithKey) //aaa
AppConfig2["text_key"] = "bbb"
print(AppConfig2.textWithKey) //bbb- 选择是否加密数据:
 
    //使用encrypt来声明加密存储数据
    var userInfo: UserInfo? by smartKey(null, encrypt = true)
- 为每个配置类设置存储实现
 
不指定
implCls时, 默认实现为JsonSettings
@Config(implCls = FileSettings::class)
class AppConfig1 {
}
@Config(implCls = PropertiesSettings::class)
class AppConfig2 {
}- 无缓存的NoCacheKey
 
由于SmartKey会对value进行缓存,在多进程会存在问题。因此而生的NoCacheKey,保证读取的数据是实时的。
使用和SmartKey基本一致。
另外,在使用基于文件存储的Settings时,修改文件配置后,NoCacheKey可以监听文件变化,来载入最新配置。
    var text: String by noCacheKey("defaultValue", key = "your_key")- JsonSettings
 
使用json格式存储配置。
- PropertiesSettings
 
基于java PropertiesSettings持久化
可设置baseDir PropertiesSettings.baseDir = "..."
- FileSettings
 
使用文件存储。
可设置baseDir FileSettings.baseDir = "..."
其中 JsonSettings 和 PropertiesSettings 继承与 BaseSyncFileSetting,配置可随文件修改重新加载到内存。为达到此目的,你需要使用 NoCacheKey 来确保实时读取到的是修改后的配置
- 实现
com.russhwolf.settings.Settings接口 
//必须存在构造函数(val configName:String)
class MySettingsImpl(val configName:String) : Settings- 使用自定义实现类
 
在配置类设置注解参数implCls:
@Config(implCls = MySettingsImpl::class)
class AppConfig {
}allprojects {
    repositories {
        //...
        maven { url 'https://jitpack.io' }
    }
}- Kotlin
 
dependencies {
    implementation "com.github.Vove7.SmartKey:smartkey:$lastest_version"
}- Android
 
dependencies {
    implementation "com.github.Vove7.SmartKey:smartkey-android:$lastest_version"
}在开启混淆时,请注意添加规则以保证SmartKey正常工作。
- 由于实体类的持久化使用了Gson,请保证实体类字段名不被混淆,或使用注解@SerializedName
 - 在没有指定key的config实例,例如 val a :Int by smaryKey(0),由于没有指定key,会默认使用变量名作为key,请保证变量名不被混淆(本人未验证kotlin编译混淆后,property name是否被改变)
 
- DynamicKey
 
this key can reset to 0 everyday:
val dk by synamicKey(0) {  
    SimpleDateFormat("yyyy-MM-dd").format(Date()) + "_today_xxx_count"
}- ExpirableKey
 
- Kotlin
 - 底层存储使用multiplatform-settings
 - Gson
 
