一个高大上的SharedPreferences工具类——XPrefs

news/2024/5/19 6:19:14 标签: android, XPrefs, 动态代理, 反射, 注解

背景

SharedPreferences常用来存储一些轻量级的数据,SharedPreferences存储的就是一个key-value(键值对)。Sharedpreferences在日常的android开发中使用的应该算是挺频繁的,通常我们开发者为了存储一个key,都会在一个类里写好对应的getter和setter方法,而且还要手动写key,也就是说写了方法还要定义key。项目写的多了以后明显会感觉到这样写很麻烦。这时候XPrefs就出现了,并很好的解决了这个问题。

XPrefs可以直接保存和读取实例对象,是SharedPreferences中的ORM,还可以通过接口来做文章,这种方式用起来是最方便的,具体介绍接着往下看。

用法

初始化

  XPrefs.bind(this);

初始化是绑定了contenxt,之后的操作就不需要传入context参数了,如果你传入activity,会自动转成application context,这样就避免了内存泄漏的问题,当然你也可以在application的类里进行绑定操作。

整存整取

 /**
     * 整存整取,把javabean类中的所有有效的字段都写入sharedpreferences中
     */
    private void saveAll() {
        //如果想切换写入的sharedpreferences文件,可以调用
//        XPrefs.changeFileName("your custom sp file's name");
        //如果设置Mode,如果调用了changeFileName方法,则必须在changeFileName之后调用
//        XPrefs.changeFileMode(Context.MODE_PRIVATE);
        //或者直接调用
//        XPrefs.changeFileNameAndMode("your custom sp file's name", Context.MODE_PRIVATE);

        XPrefs.changeFileName(spFile1);
        UserBean userBean = new UserBean();
        userBean.setAge(21);
        userBean.setFuns(1000);
        userBean.setMoney(100);
        userBean.setName("韩梅梅");
        userBean.setVIP(true);
        XPrefs.saveAll(userBean);
        //读取,查看存入的数据
        userBean = XPrefs.get(UserBean.class);
        LogUtils.i("saveAll " + userBean);
    }

上面代码中的changeFileName和changeFileMode方法分别是设置Sharedpreferences文件的name和mode的,要注意的是,如果调用了changeFileName,那么需要调用changeFileMode的话就必须要在changeFileName之后调用。如果不设置name,默认操作的文件名是XPrefs,默认的mode是Context.MODE_PRIVATE。

接着new了一个UserBean的实例,给需要保存的属性设置了对应的值,然后调用saveAll就把UserBean中所有的属性都保存进了SharedPreferences文件里,其中key是属性名,value是属性的值。让我们接着看看UserBean这个类,

    /**
     * final 修饰的字段不会被存储
     */
    private final String InvalidField="InvalidField";
    /**
     * 添加了XIgnore注解的字段会被忽视,也不会被存储
     */
    @XIgnore
    private String IgnoreField="IgnoreField";
    //目前支持以下几种类型的数据存储
    private String name;
    private float money;
    private int age;
    private boolean isVIP;
    private long funs;

   ...省略一些getter、setter方法和toString()...

用final修饰的属性和加上@XIgnore注解的属性都是被忽略的,不会被存储的。存储的时候,key是属性名(如”name”,”money”,”age”…),value是属性的值。最后通过下面这行代码读取所有存入的数据,直接调用userbean的getter方法,就可以使用这些数据。
userBean = XPrefs.get(UserBean.class);
整存整取的好处除了方便以外,就是效率高,无论存储了多少属性,都只操作了一次文件,有点类似于数据库中的事物。

既然可以保存和读取整个javaBean,那么也应该可以对javaBean中的单个属性进行存储和读取。

单个字段的存储和读取

 /**
     * 单个字段的存储和读取
     */
    public void save() {
        //修改存储的sharedpreferences文件,mode默认为Context.MODE_PRIVATE
        XPrefs.changeFileName(spFile2);
        UserBean userBean = new UserBean();
        userBean.setAge(42);
        userBean.setFuns(2000);
        userBean.setMoney(200);
        userBean.setName("李雷");
        userBean.setVIP(false);
        XPrefs.save(userBean, "name");
        XPrefs.save(userBean, "age");
        XPrefs.save(userBean, "funs");
        XPrefs.save(userBean, "money");
        XPrefs.save(userBean, "isVIP");
        //读取,查看存入的数据
        Class cls = UserBean.class;
        boolean isVIP = XPrefs.getBoolean(cls, "isVIP");
        String name = XPrefs.getString(cls, "name");
        int age = XPrefs.getInt(cls, "age");
        long funs = XPrefs.getLong(cls, "funs");
        float money = XPrefs.getFloat(cls, "money");
        LogUtils.i("save name=" + name + ";money=" + money + ";age=" + age + ";funs=" + funs + ";isVIP=" + isVIP);
        //整取
        userBean = XPrefs.get(UserBean.class);
        LogUtils.i("save " + userBean.toString());
    }

上面的代码演示了对UserBean中的字段单独进行读写操作。

XPrefs.save(userBean, "name");
String name = XPrefs.getString(cls, "name");

要注意的save和getString方法传入的第二个参数得和属性名相同,才能达到想要的效果。也就是如果定义了一个属性private String name;那么传入的就得是“name”。看上去有点麻烦,确实挺麻烦的,但这只是一种用法,更方便的用法后面会介绍。

注解">使用接口和注解

    private void saveAllAndFollowYourHeart() {
        IUser user = XPrefs.getObject(IUser.class);
        user.setName("Tom");
        user.setAge(18);
        user.setFuns(4000);
        user.setMoney(40000);
        user.setVip(true);
        LogUtils.i("IUser name=" + user.getName() + ";money=" + user.getMoney() + ";age=" + user.getAge()+ ";funs=" + user.getFuns() + ";isVIP=" + user.getVip());
    }

首先,调用了XPrefs.getObject(IUser.class)拿到了接口IUser的一个实例对象user,接着分别调用了user的set和get方法,看上去没什么,但是,在执行set方法的时候就已经把数据 存了起来,执行get方法就是把数据读取出来。
具体让我们看看IUser接口,

@XSPFile(fileName = "IUser", fileMode = Context.MODE_PRIVATE)
public interface IUser {
    @XSet(key = "name", fileName = "IUser", fileMode = Context.MODE_PRIVATE)
    void setName(String name);

    @XGet(key = "name")
    String getName();

    @XSet(key = "age")
    void setAge(int age);

    @XGet(key = "age")
    int getAge();

    @XSet(key = "funs")
    void setFuns(int funs);

    @XGet(key = "funs")
    int getFuns();

    @XSet(key = "vip")
    void setVip(boolean vip);

    @XGet(key = "vip")
    boolean getVip();

    @XSet(key = "money")
    void setMoney(float money);

    @XGet(key = "money")
    float getMoney();
}

有三个注解XSPFile,XSet和XGet,这三个注解都不是必需的。其中XSPFile是用来指定file name和mode的,用来标记接口,作用域是类。

@XSPFile(fileName = "IUser", fileMode = Context.MODE_PRIVATE)

XSet和XGet则是来标记方法的作用的,一个方法用XSet标记了,那么这个方法的作用就是写入数据到指定的SharedPreferences文件中的,其中key就是写入时的key。XSet也能指定写入的文件 name和mode,如果XSPFile和XSet同时指定了文件的name和mode,那么以XSet指定的为准。

@XSet(key = "name", fileName = "IUser", fileMode = Context.MODE_PRIVATE)
void setName(String name);

如果一个方法用XGet标记了,那么这个方法的作用就是从SharedPreferences文件中读取数据,XGet中的key就是读取时的key,其他的和XSet一样。

 @XGet(key = "name")
 String getName();

这样也不是很好,每写一个方法就得加一个注解,好麻烦的说,所以还可以更简单点。

注解">使用接口不用注解

private void saveAllAndFollowYourHeartToo() {
        IEmployee employee = XPrefs.getObject(IEmployee.class);
        employee.setName("员工");
        employee.setAge(22);
        employee.setSalary(3000);
        LogUtils.i("IEmployee name=" + employee.getName() + ";age=" + employee.getAge() + ";salary=" + employee.getSalary());
    }

这里的用法和用注解的是一样的,主要还是IEmployee接口上的区别,

@XSPFile(fileName = "IEmployee", fileMode = Context.MODE_PRIVATE)
public interface IEmployee {
    /**
     * 存储字段 name
     *
     * @param name value
     */
    void setName(String name);

    /**
     * 读取字段 name
     *
     * @return
     */
    String getName();

    void setAge(int age);

    int getAge();

    void setSalary(float salary);

    float getSalary();
}

可以看到,不用注解以后,代码少了将近一半。
这个时候key主要是通过解析方法名来得到的,所以一个方法以set开头的方法的作用就是写入数据到指定的SharedPreferences文件中,其中key就是方法名除去set的后半部分,且首字母小写,举个例子,方法setName的key就是name;
那么一个以get开头的方法的作用也是很明显了,就是从SharedPreferences文件中读取数据,其中key就是方法名出去get的后半部分,首字母也是小写,举个例子,方法getName的key就是name。看上去已经很方便了,但是这都不算什么,更骚的在后面。

最后一种用法

 private void saveAllAndFollowYourHeartThree() {
        IStudent student = XPrefs.getObject(IStudent.class);
        student.name("学生3号");
        student.score(100);
        student.sex("女");
        LogUtils.i("IStudent name=" + student.name() + ";score=" + student.score() + ";sex=" + student.sex());
    }

这里的用法和上面两种依然没有什么区别,主要的区别还是在IStudent接口上,

@XSPFile(fileName = "IStudent")
public interface IStudent {
    /**
     * 存储字段 name
     *
     * @param name value
     */
    void name(String name);

    /**
     * 读取字段 name
     *
     * @return
     */
    String name();

    void score(int score);

    int score();

    void sex(String sex);

    String sex();
}

和上种用法相比,区别就是方法名中没有了set和get,那么这是怎么判断方法的作用和要存入数据的key的呢?首先,key就是方法名,完完全全的懒人的写法;其次,方法的作用是通过方法参数和方法返回值来判断的,

  1. 如果一个方法有一个参数,那么这个方法的作用就是写入数据到指定的SharedPreferences文件中,key是方法名,值是传入的参数;
  2. 如果一个方法没有参数并且方法的返回值不是void,那么这个方法的作用就是从指定的SharedPreferences文件中读取数据,key是方法名。

这几种通过接口来做文章的写法,XPrefs内部是通过动态代理来做的,java对动态代理的支持仅限于接口,所以用的是接口,如果不用接口的话就得引入第三方的库,这种我也做过,但是项目的体积就会变得更大,最终我放弃了。

混淆注意事项

  • 不要混淆XPrefs中的注解类型,添加混淆配置:
      -keep class * extends java.lang.annotation.Annotation { *; }
  • 对于用于持久化的实体类不要混淆,包含javaBean和接口,在demo中是这样的:
      -keep interface com.huxq17.xprefs.example.interfaces.** { *; }
      -keepclasseswithmembers class com.huxq17.xprefs.example.UserBean {
        <fields>;
        <methods>;
      }

具体根据自己项目情况而定。

最后

XPrefs的地址:点击我,这次这个项目的灵感来源于github上的一个项目,当时我就在思考怎么做出一个简单易用的SharedPreferences工具类,为了寻找灵感我就在github上面乱逛,一次偶然的机会我看到了这个项目,阅读了项目里的代码,思路立马就清晰了,项目地址:androidInject,非常感谢这个项目的作者。


http://www.niftyadmin.cn/n/1636837.html

相关文章

邻接表(无向图)

邻接表&#xff08;无向图&#xff09;转载于:https://www.cnblogs.com/LoveFishC/archive/2013/05/04/3846861.html

由拉钩app引起的故事

前言 很偶然的一个机会&#xff0c;在用拉钩app的时候发现了一个上拉加载更多的效果&#xff0c;就是列表到达最底部时继续上拉&#xff0c;底部文字会由点击加载变成松开载入。我感觉这个效果不错&#xff0c;所以给XRefreshView加了这个功能&#xff0c;先上图。 用法说明…

大大的蛋项目 第二篇 第三关

大大的蛋项目 第二篇 这个项目的UI要自动适应分辨率&#xff0c;我们采用了Unity3d自带的UI&#xff0c;对于GUI的自适应原则是这样的&#xff01; GUI.matrix Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, new Vector3(m_fScaleWidth, m_fScaleHeight, 1)); 其…

让多种类型item的Recyclerview能够上拉和下拉刷新--XRefreshView

什么是XRefreshView XRefreshView是一款支持任何view上拉加载和下拉刷新的库&#xff0c;之所以说是任何view&#xff0c;是因为他不仅在内部适配了Recyclerview&#xff0c;abslistview&#xff0c;scrollview&#xff0c;webview等view&#xff0c;还提供了让你自己去判断vi…

深入理解 typedef 与 #define

无论是C&#xff0c;还是C&#xff0c;我们经常发现 typedef 关键字的身影。typedef 与 #define 有些相似&#xff0c;但更多的是不同&#xff0c;特别是在一些复杂的用法上&#xff0c;就完全不同了。 首先&#xff0c;看一下 typedef 的用法&#xff1a; 1、typedef用法总结…

Android实用的任务管理器—tractor

在平时的android开发工作中&#xff0c;我们经常需要执行耗时操作&#xff0c;有时为了用户体验还需要显示个等待框&#xff0c;我之前的做法都是开一个线程&#xff0c;然后用handler发消息进行显示和关闭等待框以及相关的ui操作。如果任务比较多的话,频繁的new Thread会让代码…

C++11-委托构造函数(新特性)

在C98中&#xff0c;如果你想让两个构造函数完成相似的事情&#xff0c;可以写两个大段代码相同的构造函数&#xff0c;或者是另外定义一个init()函数&#xff0c;让两个构造函数都调用这个init()函数。例如&#xff1a; class X {int a;// 实现一个初始化函数validate(int x)…

nullnullAndroid Interface Definition Language (AIDL) 接口描述语言

上班之余抽点时间出来写写博文&#xff0c;希望对新接触的朋友有帮助。今天在这里和大家一起学习一下nullnull AIDL (Android Interface Definition Language) is similar to other IDLs you might have worked with. It allows you to define the programming interface that …