代码拉取完成,页面将自动刷新
由于功能框架与业务代码的生命周期不同且功能框架与业务代码耦合紧密,导致无法更换过时的功能框架。 为了解决类似的问题我决定做一个搬运工的操作,将所有常用的框架与业务代码完全分离, 并提供一个比较友好的开发环境。已封装的框架有
本章主要介绍SP缓存框架 gitee的链接地址:https://gitee.com/GZona/zsp
框架优点:
1、保持本地与缓存的数据同步
2、崩溃后可以对缓存数据进行还原
3、操作简单,以接口的形式存储数据,增加数据的可读性
4、可对缓存数据进行第一次加工
5、业务相关的代码与功能框架完全隔离,当需要更新框架直接修改配置就可以
/**
* 从SP中获取数据
* @param context 上下文
* @param spName SP文件名
* @param key 取出数据的Key值
*/
fun getValue(context: Context, spName: String, key: String) {
val sharedPreferences: SharedPreferences =
context.getSharedPreferences(spName, Context.MODE_PRIVATE)
sharedPreferences.getString(key, "")
}
/**
* 将数据存入SP中
* @param context 上下文
* @param spName SP文件名
* @param key 存入数据的Key值
* @param value 存入的数据
*/
fun setValue(context: Context, spName: String, key: String, value: String) {
val sharedPreferences: SharedPreferences =
context.getSharedPreferences(spName, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putString(key, value)
editor.apply()
}
该套流程是我们最常见的一种使用方式,但是这样写有许多的问题,其中最重要的两个问题是
通过代理模式产生的作用, 1、监听setValue方法,写入数据到内存的时候会同步保存到本地, 2、监听getValue方法,当读取数据时如果内存数据为空时获取默认数据(从本地缓存拉取数据),如果内存不为空则直接返回内存数据
1、写代理类,监听getValue和setValue
/**
* Create by LiJie at 2019-05-29
* 通过代理从[SharedPreferences]获取或保存变量,,ReadWriteProperty读写代理,当调用get、和get时可以触发相应事件
* var account by SharedPref(default = "")
*/
class SharedPref<T>(
private val default: T,
private val keyName: String = "",
spName: String = "shared_pref",
context: Context = CommonLibApp.appContext
) : ReadWriteProperty<Any, T> {
private val sharedPreferences: SharedPreferences = context.getSharedPreferences(spName, Context.MODE_PRIVATE)
override fun getValue(thisRef: Any, property: KProperty<*>): T {
val name = if (keyName.isEmpty()) property.name else keyName
return with(sharedPreferences) {
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
when (default) {
is String -> getString(name, default) as T
is Int -> getInt(name, default) as T
is Float -> getFloat(name, default) as T
is Boolean -> getBoolean(name, default) as T
is Long -> getLong(name, default) as T
else -> throw IllegalArgumentException("not support type")
}
}
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
val name = if (keyName.isEmpty()) property.name else keyName
val editor = sharedPreferences.edit()
with(editor) {
@Suppress("IMPLICIT_CAST_TO_ANY")
when (value) {
is String -> putString(name, value)
is Int -> putInt(name, value)
is Float -> putFloat(name, value)
is Boolean -> putBoolean(name, value)
is Long -> putLong(name, value)
else -> throw IllegalArgumentException("not support type")
}
}
editor.apply()
}
}
2、引用代理,实现内存与本地缓存同步
private var uuid_: String? by SharedPref(
default = "",
keyName = "uuid",
spName = SP_NAME
)
//Delegates.observable的作用是监听数据的指针,如果指针发生了变化则触发相应事件,以下列数据为例,
//当uuid为null时回去拉取uuid_的数据(即获取数据SP缓存),当uuid发生改变的时候,会触发下列回调事件(即数据写入SP缓存)
var uuid by Delegates.observable(
uuid_
) { _, _, newValue ->
uuid_ = newValue
}
常见的SP缓存问题解决了,但是又有新的问题出现了
(1)为了一个数据的缓存编写的代码过多
(2)当需要更换其他SP缓存框架的时候比较费劲
(3)支持的数据类型有限
(4)功能框架与业务逻辑分离的还不够彻底
将重复的代码通过编译时自动生成,提供开发效率,彻底隔绝功能框架与业务逻辑,通过接口统一管理同模块数据,方便阅读和理解
val TestPref: ITest by lazy { SharePrefHolder.getSpClass(ITest::class.java) }
@AnSharedPref()
interface ITest {
var test1: String
}
其数据最终以接口的形式存在,并且以全局静态变量的方式调用,其优势 1、代码简单 2、通过单独一个接口来集合数据,可以做到更好的归纳,方便梳理数据 3、由于SP缓存框架已经被封装,所以我们可以当有性能更好的框架时可以做到无感切换 4、增加了更多的数据存储类型,支持接口的存储、数据中含有接口的存储、数据中的抽象集合中的抽象接口数据存储
//1、将它添加到存储库末尾的根 build.gradle 中
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
...................
//2、在模块对于的build.gradle 中
plugins {
id 'com.android.application'
id 'kotlin-android'
//1、支持kapt,这一步非常重要
id "kotlin-kapt"
}
...................
//3、Java语音版本支持
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
...................
//4、导入库
dependencies{
//3、导入开发jar包
//SP工具框架
//用MMKV本地缓存
implementation 'com.gitee.GZona.zsp:lib_mmkv:v1.1.04'
//用系统自带的本地缓存SP存储数据(与MMKV只需要实现一个就可以)
//implementation 'com.gitee.GZona.zsp:lib_sp:v1.1.04'
//SP的注解框架
implementation 'com.gitee.GZona.zsp:lib_sp_annotation:v1.1.04'
//SP的注解解析框架
kapt 'com.gitee.GZona.zsp:lib_sp_annotation:v1.1.04'
}
...................
class App : Application() {
override fun onCreate() {
super.onCreate()
//由于SP需要使用到context,所以在使用前需要提前需要初始化
SharePrefHolder.init { this }
}
}
package com.zona.yhsp
import android.text.TextPaint
import android.widget.TextView
import com.zona.lib_sp.SharePrefHolder
import com.zona.lib_sp_annotation.AnSharedPref
import com.zona.lib_sp_annotation.AnSpField
val TestPref: ITest by lazy { SharePrefHolder.getSpClass(ITest::class.java) }
/**
* @ClassName Test
* @Description
* @Author zona
* @Date 2021/3/31 16:44
* @Version 1.0
*/
@AnSharedPref()
interface ITest {
var test1: String
@AnSpField( defaultValue = "添加的默认数据")
var test8: String
var test2: Int
var test3: Float
var test4: Double
var test5: Boolean
var test6: List<TextView>?
var test7: List<Map<String, HashMap<Int, TextPaint>>>?
var test8: Test?
fun d(i: Int): String {
return "测试数据:${i}"
}
fun text(): String {
return test1
}
}
~~~~~~
class Test(val ddd:String) {
var texs1: String? = null
var texs2: String = ""
var aaaa: Int = 0
}
~~~~~~
应用示例
setContentView(R.layout.layout_test)
val content1 = findViewById<TextView>(R.id.content1)
val content2 = findViewById<TextView>(R.id.content2)
val btn1_1 = findViewById<TextView>(R.id.btn1_1)
val btn1_2 = findViewById<TextView>(R.id.btn1_2)
val btn2_1 = findViewById<TextView>(R.id.btn2_1)
val btn2_2 = findViewById<TextView>(R.id.btn2_2)
content1.text = TestPref.test1
content2.text = TestPref.test8
btn1_1.setOnClickListener {
content1.text = btn1_1.text
TestPref.test1 = btn1_1.text.toString()
}
btn1_2.setOnClickListener {
content1.text = btn1_2.text
TestPref.test1 = btn1_2.text.toString()
Toast.makeText(this, TestPref.text(), Toast.LENGTH_SHORT).show()
}
TestPref:test8 = Test("测试")
btn2_2.setOnClickListener {
//如果需要修改对象中的数据,可以使用该方法同步
TestPref:test8.saveSpData {
DomeConf.test?.aaaa = (DomeConf.test?.aaaa ?: 0) + 1
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。