SharedPreferences經(jīng)常使用來(lái)存儲(chǔ)1些輕量級(jí)的數(shù)據(jù),SharedPreferences存儲(chǔ)的就是1個(gè)key-value(鍵值對(duì))。Sharedpreferences在平常的android開(kāi)發(fā)中使用的應(yīng)當(dāng)算是挺頻繁的,通常我們開(kāi)發(fā)者為了存儲(chǔ)1個(gè)key,都會(huì)在1個(gè)類里寫好對(duì)應(yīng)的getter和setter方法,而且還要手動(dòng)寫key,也就是說(shuō)寫了方法還要定義key。項(xiàng)目寫的多了以后明顯會(huì)感覺(jué)到這樣寫很麻煩。這時(shí)候候XPrefs就出現(xiàn)了,并很好的解決了這個(gè)問(wèn)題。
XPrefs可以直接保存和讀取實(shí)例對(duì)象,是SharedPreferences中的ORM,還可以通過(guò)接口來(lái)做文章,這類方式用起來(lái)是最方便的,具體介紹接著往下看。
XPrefs.bind(this);
初始化是綁定了contenxt,以后的操作就不需要傳入context參數(shù)了,如果你傳入activity,會(huì)自動(dòng)轉(zhuǎn)成application context,這樣就避免了內(nèi)存泄漏的問(wèn)題,固然你也能夠在application的類里進(jìn)行綁定操作。
/**
* 整存整取,把javabean類中的所有有效的字段都寫入sharedpreferences中
*/
private void saveAll() {
//如果想切換寫入的sharedpreferences文件,可以調(diào)用
// XPrefs.changeFileName("your custom sp file's name");
//如果設(shè)置Mode,如果調(diào)用了changeFileName方法,則必須在changeFileName以后調(diào)用
// XPrefs.changeFileMode(Context.MODE_PRIVATE);
//或直接調(diào)用
// 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);
//讀取,查看存入的數(shù)據(jù)
userBean = XPrefs.get(UserBean.class);
LogUtils.i("saveAll " + userBean);
}
上面代碼中的changeFileName和changeFileMode方法分別是設(shè)置Sharedpreferences文件的name和mode的,要注意的是,如果調(diào)用了changeFileName,那末需要調(diào)用changeFileMode的話就必須要在changeFileName以后調(diào)用。如果不設(shè)置name,默許操作的文件名是XPrefs,默許的mode是Context.MODE_PRIVATE。
接著new了1個(gè)UserBean的實(shí)例,給需要保存的屬性設(shè)置了對(duì)應(yīng)的值,然后調(diào)用saveAll就把UserBean中所有的屬性都保存進(jìn)了SharedPreferences文件里,其中key是屬性名,value是屬性的值。讓我們接著看看UserBean這個(gè)類,
/**
* final 修飾的字段不會(huì)被存儲(chǔ)
*/
private final String InvalidField="InvalidField";
/**
* 添加了XIgnore注解的字段會(huì)被忽視,也不會(huì)被存儲(chǔ)
*/
@XIgnore
private String IgnoreField="IgnoreField";
//目前支持以下幾種類型的數(shù)據(jù)存儲(chǔ)
private String name;
private float money;
private int age;
private boolean isVIP;
private long funs;
...省略1些getter、setter方法和toString()...
用final修飾的屬性和加上@XIgnore注解的屬性都是被疏忽的,不會(huì)被存儲(chǔ)的。存儲(chǔ)的時(shí)候,key是屬性名(如”name”,”money”,”age”…),value是屬性的值。最后通過(guò)下面這行代碼讀取所有存入的數(shù)據(jù),直接調(diào)用userbean的getter方法,就能夠使用這些數(shù)據(jù)。
userBean = XPrefs.get(UserBean.class);
整存整取的好處除方便之外,就是效力高,不管存儲(chǔ)了多少屬性,都只操作了1次文件,有點(diǎn)類似于數(shù)據(jù)庫(kù)中的事物。
既然可以保存和讀取全部javaBean,那末也應(yīng)當(dāng)可以對(duì)javaBean中的單個(gè)屬性進(jìn)行存儲(chǔ)和讀取。
/**
* 單個(gè)字段的存儲(chǔ)和讀取
*/
public void save() {
//修改存儲(chǔ)的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");
//讀取,查看存入的數(shù)據(jù)
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());
}
上面的代碼演示了對(duì)UserBean中的字段單獨(dú)進(jìn)行讀寫操作。
XPrefs.save(userBean, "name");
String name = XPrefs.getString(cls, "name");
要注意的save和getString方法傳入的第2個(gè)參數(shù)得和屬性名相同,才能到達(dá)想要的效果。也就是如果定義了1個(gè)屬性private String name;那末傳入的就得是“name”。看上去有點(diǎn)麻煩,確切挺麻煩的,但這只是1種用法,更方便的用法后面會(huì)介紹。
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());
}
首先,調(diào)用了XPrefs.getObject(IUser.class)拿到了接口IUser的1個(gè)實(shí)例對(duì)象user,接著分別調(diào)用了user的set和get方法,看上去沒(méi)甚么,但是,在履行set方法的時(shí)候就已把數(shù)據(jù) 存了起來(lái),履行g(shù)et方法就是把數(shù)據(jù)讀取出來(lái)。
具體讓我們看看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();
}
有3個(gè)注解XSPFile,XSet和XGet,這3個(gè)注解都不是必須的。其中XSPFile是用來(lái)指定file name和mode的,用來(lái)標(biāo)記接口,作用域是類。
@XSPFile(fileName = "IUser", fileMode = Context.MODE_PRIVATE)
XSet和XGet則是來(lái)標(biāo)記方法的作用的,1個(gè)方法用XSet標(biāo)記了,那末這個(gè)方法的作用就是寫入數(shù)據(jù)到指定的SharedPreferences文件中的,其中key就是寫入時(shí)的key。XSet也能指定寫入的文件 name和mode,如果XSPFile和XSet同時(shí)指定了文件的name和mode,那末以XSet指定的為準(zhǔn)。
@XSet(key = "name", fileName = "IUser", fileMode = Context.MODE_PRIVATE)
void setName(String name);
如果1個(gè)方法用XGet標(biāo)記了,那末這個(gè)方法的作用就是從SharedPreferences文件中讀取數(shù)據(jù),XGet中的key就是讀取時(shí)的key,其他的和XSet1樣。
@XGet(key = "name")
String getName();
這樣也不是很好,每寫1個(gè)方法就得加1個(gè)注解,好麻煩的說(shuō),所以還可以更簡(jiǎn)單點(diǎn)。
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());
}
這里的用法和用注解的是1樣的,主要還是IEmployee接口上的區(qū)分,
@XSPFile(fileName = "IEmployee", fileMode = Context.MODE_PRIVATE)
public interface IEmployee {
/**
* 存儲(chǔ)字段 name
*
* @param name value
*/
void setName(String name);
/**
* 讀取字段 name
*
* @return
*/
String getName();
void setAge(int age);
int getAge();
void setSalary(float salary);
float getSalary();
}
可以看到,不用注解以后,代碼少了將近1半。
這個(gè)時(shí)候key主要是通過(guò)解析方法名來(lái)得到的,所以1個(gè)方法以set開(kāi)頭的方法的作用就是寫入數(shù)據(jù)到指定的SharedPreferences文件中,其中key就是方法名除去set的后半部份,且首字母小寫,舉個(gè)例子,方法setName的key就是name;
那末1個(gè)以get開(kāi)頭的方法的作用也是很明顯了,就是從SharedPreferences文件中讀取數(shù)據(jù),其中key就是方法名出去get的后半部份,首字母也是小寫,舉個(gè)例子,方法getName的key就是name。看上去已很方便了,但是這都不算甚么,更騷的在后面。
private void saveAllAndFollowYourHeartThree() {
IStudent student = XPrefs.getObject(IStudent.class);
student.name("學(xué)生3號(hào)");
student.score(100);
student.sex("女");
LogUtils.i("IStudent name=" + student.name() + ";score=" + student.score() + ";sex=" + student.sex());
}
這里的用法和上面兩種仍然沒(méi)有甚么區(qū)分,主要的區(qū)分還是在IStudent接口上,
@XSPFile(fileName = "IStudent")
public interface IStudent {
/**
* 存儲(chǔ)字段 name
*
* @param name value
*/
void name(String name);
/**
* 讀取字段 name
*
* @return
*/
String name();
void score(int score);
int score();
void sex(String sex);
String sex();
}
和上種用法相比,區(qū)分就是方法名中沒(méi)有了set和get,那末這是怎樣判斷方法的作用和要存入數(shù)據(jù)的key的呢?首先,key就是方法名,完完全全的懶人的寫法;其次,方法的作用是通過(guò)方法參數(shù)和方法返回值來(lái)判斷的,
這幾種通過(guò)接口來(lái)做文章的寫法,XPrefs內(nèi)部是通過(guò)動(dòng)態(tài)代理來(lái)做的,java對(duì)動(dòng)態(tài)代理的支持僅限于接口,所以用的是接口,如果不用接口的話就得引入第3方的庫(kù),這類我也做過(guò),但是項(xiàng)目的體積就會(huì)變得更大,終究我放棄了。
-keep class * extends java.lang.annotation.Annotation { *; }
-keep interface com.huxq17.xprefs.example.interfaces.** { *; }
-keepclasseswithmembers class com.huxq17.xprefs.example.UserBean {
<fields>;
<methods>;
}
具體根據(jù)自己項(xiàng)目情況而定。
XPrefs的地址:點(diǎn)擊我,這次這個(gè)項(xiàng)目的靈感來(lái)源于github上的1個(gè)項(xiàng)目,當(dāng)時(shí)我就在思考怎樣做出1個(gè)簡(jiǎn)單易用的SharedPreferences工具類,為了尋覓靈感我就在github上面亂逛,1次偶然的機(jī)會(huì)我看到了這個(gè)項(xiàng)目,瀏覽了項(xiàng)目里的代碼,思路立馬就清晰了,項(xiàng)目地址:androidInject,非常感謝這個(gè)項(xiàng)目的作者。