Kotlin优雅的使用sp(SharedPreferences)
前言
我平時用java寫的sp工具類,現在有兩個需求:
第一個是要管理sp的文件名,雖然java可以通過Config的方式配置幾個final靜態的字符串常量來管理,但是總感覺不夠優雅,而且可能存在隨便寫個文件名,不放在Config內的情況
第二個是要一次保存多個數據的話,java需要多次調用,emmm,不夠優雅
效果
還是先看kt調用的代碼
同時向USER_INFO_CACHE文件中寫入兩個數據
從USER_INFO_CACHE文件中讀取數據,并設置默認值
清空USER_INFO_CACHE文件中的數據
同時從USER_INFO_CACHE文件讀取多條String數據
使用屬性委托,快捷操作sp中的數據
代碼
工具類代碼,直接粘貼到一個kt文件內
重構后的:
/*** creator: lt 2019/7/13--15:40 lt.dygzs@qq.com* effect : SharedPreferences工具類* warning:*//*** 往sp中寫入數據,this為fileName*/ fun SpName.writeSP(bean: Pair<String, *>): SpName {App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE).edit().put(bean).apply()return this }fun SpName.writeSPs(vararg beans: Pair<String, *>): SpName {if (beans.isEmpty()) {throw RuntimeException("writeSPs沒有填寫可變參數")}val editor = App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE).edit()beans.map(editor::put)editor.apply()return this }//處理存儲類型 private fun SharedPreferences.Editor.put(bean: Pair<String, *>): SharedPreferences.Editor {when (bean.second) {is Boolean -> this.putBoolean(bean.first, bean.second as Boolean)is Int -> this.putInt(bean.first, bean.second as Int)is Long -> this.putLong(bean.first, bean.second as Long)is Float -> this.putFloat(bean.first, bean.second as Float)is String -> this.putString(bean.first, bean.second as String)is Number -> this.putString(bean.first, bean.second.toString())else -> this.putString(bean.first, bean.second.toJson())//toJson()是利用Gosn或者fastJson把對象變為json數據}return this }/*** 從sp中讀取數據,this為fileName* 注意讀取數值和boolean的時候最好給個默認值*/ fun <T> SpName.readSP(key: String, defaultValue: T): T {val preference = App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE)return when (defaultValue) {is Boolean -> preference.getBoolean(key, defaultValue)is Int -> preference.getInt(key, defaultValue)is Long -> preference.getLong(key, defaultValue)is Float -> preference.getFloat(key, defaultValue)is String -> preference.getString(key, defaultValue)is Byte -> preference.getString(key, defaultValue.toString()).toByte()is Short -> preference.getString(key, defaultValue.toString()).toShort()is Double -> preference.getString(key, defaultValue.toString()).toDouble()is UByte -> preference.getString(key, defaultValue.toString()).toUByte()is UShort -> preference.getString(key, defaultValue.toString()).toUShort()is UInt -> preference.getString(key, defaultValue.toString()).toUInt()is ULong -> preference.getString(key, defaultValue.toString()).toULong()else -> throw RuntimeException("如果讀取對象,請使用string類型的默認值,或者使用內聯的readSPOfAny方法")} as T }/*** 從sp中讀取對象,this為fileName* 注意,如果json讀不到則會返回null對象*/ inline fun <reified T> SpName.readSPOfAny(key: String): T? {if (T::class.java == String::class.java|| T::class.java == Boolean::class.java|| T::class.java == Number::class.java)throw RuntimeException("讀取sp中的數據時,類型不正確,readSPOfAny方法只能用來讀取對象")return this.readSP(key, "").json2Any() }/*** 同時讀取多條數據* 注意:需要讀取同一種類型,若讀取不同類型需要分開讀取* 傳入的Bean的key為key value為默認值*/ inline fun <reified T> SpName.readSPs(vararg beans: Pair<String, T>): Array<T> {if (beans.isEmpty()) {throw RuntimeException("readSPs沒有填寫可變參數")}return Array(beans.size) {this@readSPs.readSP(beans[it].first, beans[it].second)} }/*** 清空該sp文件中的所有內容*/ fun SpName.clearSP(): SpName {App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE).edit().clear().apply()return this }/*** 移除相應key的數據*/ fun SpName.removeSP(vararg keys: String): SpName {if (keys.isEmpty()) {throw RuntimeException("removeSP沒有填寫可變參數")}val edit = App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE).edit()keys.map(edit::remove)edit.apply()return this }/*** 快捷創建一個s - t 的Pair 使用方式:s/t*/ operator fun <T> String.div(value: T): Pair<String, T> = Pair(this, value)重構前的(看你怎么選了):
/*** creator: lt 2019/7/13--15:40 lt.dygzs@qq.com* effect : SharedPreferences工具類* warning: 暫時只支持 int boolean String 三種類型*/ //todo 注:下面的App.getInstance()方法是獲取Application的上下文,童鞋使用時請自行替換 /*** 往sp中寫入數據,this為fileName* 注意只接收 int String boolean*/ fun SpName.writeSP(vararg beans: SPSaveBean<*>) {val editor = App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE).edit()beans.map {when (it.value) {is Int -> editor.putInt(it.key, it.value as Int)is Boolean -> editor.putBoolean(it.key, it.value as Boolean)is String -> editor.putString(it.key, it.value as String)else -> throw RuntimeException("向sp中寫入時,類型不正確")}}editor.apply() }/*** 從sp中讀取數據,this為fileName* 注意只能讀到 int String boolean* 注意讀取int 和 boolean的時候最好給個默認值*/ fun <T> SpName.readSP(key: String, defaultValue: T): T {val preference = App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE)return when (defaultValue) {is Int -> preference.getInt(key, defaultValue)is Boolean -> preference.getBoolean(key, defaultValue)is String -> preference.getString(key, defaultValue)else -> throw RuntimeException("讀取sp中的數據時,類型不正確")} as T }inline fun <reified T> SpName.readSP(key: String): T =this.readSP(key, when (T::class.java) {Int::class.java -> 0Boolean::class.java -> falseString::class.java -> ""else -> throw RuntimeException("讀取sp中的數據時,類型不正確")} as T)/*** 同時讀取多條數據* 注意:需要讀取同一種類型,若讀取不同類型需要分開讀取* 傳入的SPSaveBean的key為key value為默認值*/ inline fun <reified T> SpName.readSPs(vararg spBeans: SPSaveBean<T>): Array<T> {return Array(spBeans.size) {return@Array this@readSPs.readSP(spBeans[it].key, spBeans[it].value)} }/*** 清空該sp文件中的所有內容*/ fun SpName.clearSP() {val sp = App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE)sp.edit().clear().apply() }/*** 移除相應key的數據*/ fun SpName.removeSP(vararg keys: String) {val sp = App.getInstance().getSharedPreferences(this, Context.MODE_PRIVATE)val edit = sp.edit()keys.map {edit.remove(it)}edit.apply() }/*** creator: lt 2019/7/13--15:54 lt.dygzs@qq.com* effect : 快捷使用SPUtil存儲的bean* warning:*/ class SPSaveBean<T>(val key: String, val value: T)/*** 快捷創建一個s - s 的SPSaveBean 使用方式:s/s 下同*/ operator fun String.div(value: String): SPSaveBean<String> = SPSaveBean(this, value)operator fun String.div(value: Int): SPSaveBean<Int> = SPSaveBean(this, value) operator fun String.div(value: Boolean): SPSaveBean<Boolean> = SPSaveBean(this, value)SPFileName文件
/*** creator: lt 2019/7/13--15:45 lt.dygzs@qq.com* effect : 存儲sp文件的名字,寫的時候加上用途* warning: 一個文件不要存儲過多內容,影響效率*/ object SPFileName {@JvmFieldval USER_INFO_CACHE = SpName("userInfoCache")//用于存儲用戶信息 }class SpName(val value: String)Kotlin委托的代碼,可以更方便的使用sp
/*** 屬性委托類,可以更方便的使用* 只可以獲取基本類型* 如果key傳空字符串,則使用自身的名字為key*/ class SP<T>(private val spName: SpName, private val defaultValue: T, private val key: String = "") {operator fun getValue(thisRef: Any?, property: KProperty<*>): T =spName.readSP(if (key == "") property.name else key, defaultValue)operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) =spName.writeSP((if (key == "") property.name else key) / value) }//只可以委托對象 class SPAny<T>(val spName: SpName, val key: String = "") {inline operator fun <reified T> getValue(thisRef: Any?, property: KProperty<*>): T? =spName.readSPOfAny(if (key == "") property.name else key)operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) =spName.writeSP((if (key == "") property.name else key) / value) }SP委托的使用
使用by關鍵字聲明一個SP委托:
獲取和設置就像操作變量一樣:
打印出:
上面的每次獲取都會從sp中獲取,而每次賦值都會向sp中寫入,比直接操作sp更方便
解析
SpUtil還是那個SpUtil,但是中間用了幾種Kotlin特性來封裝了一下,操作和管理更方便
可能有幾個可能不是太常見的地方,我簡單說一下
1.SP的性能問題
sp有性能問題,這是一般都知道的,由于底層是使用的xml文件存儲數據,所以冷讀取和冷存儲都是需要通過io流的,其xml文件存儲在 data/data/包名/shared_prefs 下,而由于sp#edit#commit方法就是冷提交:在主線程中直接阻塞并提交數據變更,所以,如果一個sp文件中存儲過多或過大的話,就有很大的可能會阻塞主線程,所以提交改用sp#edit#apply方法提交,是一個異步提交方法,具體實現可以自行百度
2. 字符串 斜杠 字符串? 是個什么寫法?
是kt的操作符重載,參考下面鏈接第六條,當然你看會了可以改成其他的操作符
https://blog.csdn.net/qq_33505109/article/details/81031791
3.typealias SpName = String 是什么意思?
是kt的類型別名,參考下面鏈接第九條
https://blog.csdn.net/qq_33505109/article/details/81031791
4.val (v1,v2)=readSPs() 是什么操作?
是kt的解構聲明,其實返回的就是一個泛型數組,參考下面鏈接第一條
https://blog.csdn.net/qq_33505109/article/details/81031791
總結
以上是生活随笔為你收集整理的Kotlin优雅的使用sp(SharedPreferences)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Studio 点击运行总
- 下一篇: 安卓多语言设置,深渊巨坑,适配7.0以上