当前位置: 移动技术网 > IT编程>移动开发>Android > Kotlin入门(25)共享参数模板

Kotlin入门(25)共享参数模板

2018年10月16日  | 移动技术网IT编程  | 我要评论

侠盗飞车罪恶都市秘籍直升机,小老乡进城,郭敬明陈学冬同游

共享参数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的共享参数操作方式。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网