SharedPreferences深入探讨

前言:

Android的五大存储,在平常的开发中都是经常用到的点,分别为SharedPreferences存储、文件存储、数据库存储、网络存储、ContentProvider存储; 近几日在使用SharedPreferences进行存储时,使用一个不常用的api(putStringSet)时,却出现一奇葩现象:首次sp存储之后,重启应用是可以看到存储的数据,第二次存储之后重启应用,数据却并没有保存;一开始以为是代码的问题,之后科学上网却发现原来是这个api的坑.


I 探索入口

在源码中看到SharedPreferences只是一个接口,里面除了Editor接口还有一些get/set之类的方法,那么实现类在哪里呢? android系统通常会往接口实现类的类名后缀加”impl”,例如WindowManager的接口实现类为WindowManagerImpl,所以找SharedPreferencesImpl类,果不其然,真的是实现类。那就看看吧!

II 源码分析:

分析要点,简单记录

  • mMap == 存储的键值对数据

  • 可以看到get之类的方法,都是从mMap中通过Key取出值,存储的类型有float、int、string、long、Set、boolean 6种;

  • mModified 是在Editor里初始化的,可能是暂时缓存的区域;因为是在执行commitToMemory方法时,最后数据被清空;当创建EditorImpl对象时才被创建;

  • if (existingValue != null && existingValue.equals(v)) { //程序的关键点在这个地方,如果条件成立,那么将直接跳出当前循环,则不会存储;equals里比较的是对象地址和内容是否相同;

  • putStringSet可以看到是存进了mModified,在执行commit方法的时候会调用commitToMemory方法;接着看commitTpMemory方法,当我们在putStringSet前先执行clear,可以看到mClear被置为true,那么在commitToMemory方法里,就可以看到mMap.clear(),表明mMap里的数据将被清空;这个时候,数据被放置在mModified里,mMap里对应的的数据为空,所以上面的if条件不成立,mcr.changesMade(保存的标识)标志为true;

源码的探索追求点到为止,在上面已经大概知道StringSet第一次保存成功,第二次保持失败的原因,大致是这样的

看如下截图,当第一次添加数据时,可以看到是生成了新的HashSet(代号Set001),而在第一次并未对mMap做任何有关的操作,所以这时候的mMap数据为空,Set001和mMap内容并不一样,if条件不成立,保存成功.

Editor的putStringSet方法
sp01


Editor提交到内存的commitToMemory方法(只截取了该方法部分实现)

sp02


HashSet的equals方法

sp03

下面是项目中第二次sp提交的代码,主要看划红圈的两部分,操作是先通过getStringSet方法获取之前存在sp里的值,可以在下方截图看到该方法返回的是mMap对象里存储的值v,即arraySet对象指向值v的地址,往arraySet添加完数据,这就相当于将数据也添加到了值v,在putStringSet时生成了新的HashSet,新的HashSet存储进mModified,此时再进行commit操作,在执行commit方法的时候会调用commitToMemory方法,在commitToMemory方法里,由于mModified和mMap对应的值的内容是相同的,导致了if条件成立,所以无法保存;

项目的代码(备注:对sp类进行工具类封装,所以getArraySet就是getStringSet方法的封装函数)
sp05


SharedPreferencesImpl类的getStringSet方法
sp04

III 解决方法

1.如下截图,可以在putStringSet方法执行之前或之后先进行clear操作,clear操作会将mClear标识置为true,在commitToMemory方法,可以看到会对mMap进行clear清空操作和设置文件保持的标识为true,即可成功保持

sp06


sp07

2.另一方法则是在getStringSet,重新生成另一对象,避免返回的对象指向mMap里的v值;

new HashSet<>(SharedPreferencesUtil.getInstance().getArraySet(Constant.COLLECT_URL, null));

总结:

  • 1.sp进行putStringSet操作的值不能是getStringSet返回的值,不然sp保存失败;
  • 2.sp进行putStringSet操作可先进行clear操作,或者put的Set对象是重新生成的,而不是从sp中get的;
  • 3.上面的分析只针对Set数据类型的保存,不包括其余的类型;

参考博文
http://blog.csdn.net/crazyman2010/article/details/51187817

附言:本人才疏学浅,欢迎多多讨论.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SharedPreferences是Android中用于存储和检索键值对的类。它提供了一种通用的框架,可以保存和检索原始数据类型的永久性键值对。您可以使用SharedPreferences来保存布尔值、浮点值、整型值、长整型和字符串等各种原始数据类型。这些数据将在多个用户会话中永久保存,即使应用程序已经终止也不会丢失。\[3\] 要使用SharedPreferences,您需要通过Context提供的getSharedPreferences(String name, int mode)方法获取SharedPreferences的实例。其中,第一个参数指定了SharedPreferences文件的名称(格式为xml文件),如果该文件不存在,则会创建一个新的文件。第二个参数指定了操作模式,例如MODE_PRIVATE表示只有本应用程序可以对该SharedPreferences文件进行读写,而MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE表示其他应用程序也可以读取或写入该文件。MODE_MULTI_PROCESS在Android 2.3之后已经弃用,可以省略。\[2\] SharedPreferences.Editor是用于编辑SharedPreferences的对象。它提供了一些主要的方法,例如clear()用于删除SharedPreferences中的所有数据,putXxx(String key, xxx value)用于向SharedPreferences存入指定key对应的数据,remove()用于删除SharedPreferences中指定key对应的数据项,commit()用于同步提交修改,apply()用于异步提交修改。\[1\] #### 引用[.reference_title] - *1* *2* *3* [SharedPreferences使用及原理](https://blog.csdn.net/qq_40959750/article/details/123337564)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值