zoukankan      html  css  js  c++  java
  • Kotlin入门(25)共享参数模板

    共享参数SharedPreferences是Android最简单的数据存储方式,常用于存取“Key-Value”键值对数据。在使用共享参数之前,要先调用getSharedPreferences方法声明文件名与操作模式,示例代码如下:

        SharedPreferences sps = getSharedPreferences("share", Context.MODE_PRIVATE);
    

    该方法的第一个参数是文件名,例子中的share表示当前的共享参数文件是share.xml;第二个参数是操作模式,一般填MODE_PRIVATE表示私有模式。
    共享参数若要存储数据则需借助于Editor类,示例的Java代码如下:

        SharedPreferences.Editor editor = sps.edit();
        editor.putString("name", "阿四");
        editor.putInt("age", 25);
        editor.putBoolean("married", false);
        editor.putFloat("weight", 50f);
        editor.commit(); 
    

    使用共享参数读取数据则相对简单,直接调用其对象的get方法即可获取数据,注意get方法的第二个参数表示默认值,示例的Java代码如下:

        String name = sps.getString("name", "");
        int age = sps.getInt("age", 0);
        boolean married = sps.getBoolean("married", false);
        float weight = sps.getFloat("weight", 0);
    

    从上述数据读写的代码可以看出,共享参数的存取操作有些繁琐,因此实际开发常将共享参数相关操作提取到一个工具类,在新的工具类中封装SharedPreferences的常用操作,下面便是一个共享参数工具类的Java代码例子:

    public class SharedUtil {
        private static SharedUtil mUtil;
        private static SharedPreferences mShared;
        
        public static SharedUtil getIntance(Context ctx) {
            if (mUtil == null) {
                mUtil = new SharedUtil();
            }
            mShared = ctx.getSharedPreferences("share", Context.MODE_PRIVATE);
            return mUtil;
        }
    
        public void writeShared(String key, String value) {
            SharedPreferences.Editor editor = mShared.edit();
            editor.putString(key, value);
            editor.commit(); 
        }
    
        public String readShared(String key, String defaultValue) {
            return mShared.getString(key, defaultValue);
        }
    }
    

    有了共享参数工具类,外部读写SharedPreferences就比较方便了,比如下面的Java代码,无论是往共享参数写数据还是从共享参数读数据,均只要一行代码:

        //调用工具类写入共享参数
        SharedUtil.getIntance(this).writeShared("name", "阿四");
        //调用工具类读取共享参数
        String name = SharedUtil.getIntance(this).readShared("name", "");
    

    不过这个工具类并不完善,因为它只支持字符串String类型的数据读写,并不支持整型、浮点数、布尔型等其它类型的数据读写。另外,如果外部需要先读取某个字段的数值,等处理完了再写回共享参数,则使用工具类也要两行代码(一行读数据、一行写数据),依旧有欠简洁。找毛病其实都是容易的,如果仍然使用Java编码,能完善的就完善,不能完善的也不必苛求了。
    之所以挑Java实现方式的毛病,倒不是因为看它不顺眼整天吹毛求疵,而是因为Kotlin有更好的解决办法。为了趁热打铁方便比较两种方式的优劣,下面开门见山直接给出Kotlin封装共享参数的实现代码例子:

    class Preference<T>(val context: Context, val name: String, val default: T) : ReadWriteProperty<Any?, T> {
    
        val prefs: SharedPreferences by lazy { context.getSharedPreferences("default", Context.MODE_PRIVATE) }
    
        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return findPreference(name, default)
        }
    
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            putPreference(name, value)
        }
    
        private fun <T> findPreference(name: String, default: T): T = with(prefs) {
            val res: Any = when (default) {
                is Long -> getLong(name, default)
                is String -> getString(name, default)
                is Int -> getInt(name, default)
                is Boolean -> getBoolean(name, default)
                is Float -> getFloat(name, default)
                else -> throw IllegalArgumentException("This type can be saved into Preferences")
            }
            return res as T
        }
    
        private fun <T> putPreference(name: String, value: T) = with(prefs.edit()) {
            //putInt、putString等方法返回Editor对象
            when (value) {
                is Long -> putLong(name, value)
                is String -> putString(name, value)
                is Int -> putInt(name, value)
                is Boolean -> putBoolean(name, value)
                is Float -> putFloat(name, value)
                else -> throw IllegalArgumentException("This type can be saved into Preferences")
            }.apply() //commit方法和apply方法都表示提交修改
        }
    }
    

    外部在使用该工具类之时,可在Activity代码中声明来自于Preference的委托属性,委托属性一旦声明,则它的初始值便是从共享参数读取的数值;后续代码若给委托属性赋值,则立即触发写入动作,把该属性的最新值保存到共享参数中。于是外部操作共享参数的某个字段,真正要书写的仅仅是下面的一行委托属性声明代码:

        //声明字符串类型的委托属性
        private var name: String by Preference(this, "name", "")
        //声明整型数类型的委托属性
        private var age: Int by Preference(this, "age", 0)
    

    既然Kotlin对共享参数的处理也如此传神,那么大家肯定很好奇,这个高大上的Preference究竟运用了哪些黑科技呢?且待笔者下面细细道来:
    一、模板类
    因为共享参数允许保存的数据类型包括整型、浮点数、字符串等等,所以Preference定义成模板类,具体的参数类型在调用之时再指定。
    除却代表模板类泛型的T,该类中还有两个与之相似的元素,分别是Any和*,各自表示不同的涵义。下面简单说明一下T、Any和*三者之间的区别:
    1、T是抽象的泛型,在模板类中用来占位子,外部调用模板类时才能确定T的具体类型;
    2、Any是Kotlin的基本类型,所有Kotlin类都从Any派生而来,故而它相当于Java里面的Object;
    3、*星号表示一个不确定的类型,同样也是在外部调用时才能确定,这点跟T比较像,但T出现在模板类的定义中,而*与模板类无关,它出现在单个函数定义的参数列表中,因此星号相当于Java里面的问号?;

    二、委托属性/属性代理
    注意到外部利用Preference声明参数字段时,后面跟着表达式“by Preference(...)”,这个by表示代理动作,早在第五章的“5.3.4 接口代理”就介绍了如何让类通过关键字by实现指定接口的代理,当时举例说明给不同的鸟类赋予不同的动作。第五章的例子是接口代理或称类代理,而这里则为属性代理,所谓属性代理,是说该属性的类型不变,但是属性的读写行为被后面的类接管了。
    为什么需要接管属性的读写行为呢?举个例子,市民每个月都要交电费,自己每月跑去电力营业厅交钱显然够呛,于是后来支持在电力网站上自助缴费,然而上网缴费仍显麻烦,因为需要用户主动上网付费,要是用户忘记就不好办了。所以很多银行都推出了“委托代扣”的业务,只要用户跟银行签约并指定委托扣费的电力账户,那么在每个月指定时间,银行会自动从用户银行卡中扣费并缴纳给指定的电力账户,如此省却了用户的人工操作。
    现实生活中的委托扣费场景,对应到共享参数这里,开发者的人工操作指的是手工编码从SharedPreferences类读取数据和保存数据,而自动操作指的是约定代理的属性自动通过模板类Preference<T>完成数据的读取和保存,也就是说,Preference<T>接管了这些属性的读写行为,接管后的操作则是模板类的getValue和setValue方法。属性被接管的行为叫做属性代理,而被代理的属性称作委托属性。

    三、关键字lazy
    模板类Preference<T>声明了一个共享参数的prefs对象,其中用到了关键字lazy,lazy的意思是懒惰,表示只在该属性第一次使用时执行初始化。联想到Kotlin还有类似的关键字名叫lateinit,意思是延迟初始化,加上lazy可以归纳出Kotlin变量的三种初始化操作,具体说明如下:
    1、声明时赋值:这是最常见的变量初始化,在声明某个变量时,立即在后面通过等号“=”给它赋予具体的值。
    2、lateinit延迟初始化:变量声明时没有马上赋值,但该变量仍是个非空变量,何时初始化由开发者编码决定。
    3、lazy首次使用时初始化:声明变量时指定初始化动作,但该动作要等到变量第一次使用时才进行初始化。
    此处的prefs对象使用lazy规定了属性值在首次使用时初始化,且初始化动作通过by后面的表达式来指定,即“{ context.getSharedPreferences("default", Context.MODE_PRIVATE) }”。连同大括号在内的这个表达式,其实是个匿名实例,它内部定义了prefs对象的初始化语句,并返回SharedPreferences类型的变量值。

    四、with函数
    with函数的书写格式形如“with(函数头语句) { 函数体语句 }”,看这架势,with方法的函数语句分为两部分,详述如下:
    1、函数头语句:头部语句位于紧跟with的圆括号内部。它先于函数体语句执行,并且头部语句返回一个对象,函数体语句在该对象的命名空间中运行;即体语句可以直接调用该对象的方法,而无需显式指定该对象的实例名称。
    2、函数体语句:体语句位于常规的大括号内部。它要等头部语句执行完毕才会执行,同时体语句在头部语句返回对象的命名空间中运行;即体语句允许直接调用头部对象的方法,而无需显式指定该对象的实例名称。
    综上所述,在模板类Preference<T>的编码过程中,联合运用了Kotlin的多项黑科技,方才实现了优于Java的共享参数操作方式。

  • 相关阅读:
    Tree(未解决。。。)
    Fractal(递归,好题)
    Scrambled Polygon(凸多边形,斜率)
    ZYB's Game(博弈)
    Dancing Stars on Me(判断正多边形)
    Hidden String(深搜)
    1043
    TEX Quotes(字符串,水)
    Candy Sharing Game(模拟搜索)
    hpu校赛--雪人的高度(离散化线段树)
  • 原文地址:https://www.cnblogs.com/aqi00/p/9801381.html
Copyright © 2011-2022 走看看