Android常考问题(2)-SharedPreferences

12 篇文章 0 订阅
10 篇文章 0 订阅

        首先说一下,Android五种存储文件方式:1.文件存储。用保存到文件中的方式进行存储,openFileInput()和openFileOutput()方法来读取设备上的文件。2.SQLite存储。通过数据库存储数据,常用的工具GreenDao,这个以后再看吧。3.ContentProvider存储数据,这一块主要用于数据共享。4.网络存储。5.SharedPreferences存储。

        这个本来不想多说的,确实比较简单。但是两年前把这个搞得滚瓜烂熟的我在半年没碰Android之后一脸懵逼得发现不会了。所以这次把学习内容记录下来,正好也是初级面试常考问题,可以顺便介绍。

SharedPreferences主要是在文件目录中生成XML文件,可以在/data/data/包名/shared_prefs目录下找到保存的文件。文件存储不同之处在于是文件中的读取和写入,主要用于保存文本内容之类的,比如游戏中的剧情文字,这种就直接放入文本文件中更好。SharedPreferences就主要用于保存系统的配置信息,例如上次登录的用户名等这种信息。存储和读取key-value类型的原始基本数据类型对。

考点:目前支持string、int、long、float、boolean等基本类型的存储。自定义的对象数据类型,不能用SharedPreferences来存储。否则相对复杂的数据,SharedPreferences会将其进行Base64编码,以String的形式存储。

SP是线程安全的但是不是进程安全的,线程安全只需要synchronized 关键字保障。源码中每次

注意SP是一个接口,所以不能直接创建实例。因此用

SharedPreferences sPreferences=context.getSharedPreferences("config", context.MODE_PRIVATE);

这种由context的方法进行创建。这样就有了一个名叫config.xml的文件在内部了。getSharedPreferences方法在源码中每次都并不是直接创建对象,它会现在缓存中查找,没有找到缓存,才会进行对象构建。因此多次 getSharedPreferences 几乎是没有代价的。

注意这段代码的参数,第一个参数是设置的文件名,第二个是context的方法,控制了文件的读写模式。可以使用 + 连接这些权限。

· Context.MODE_PRIVATE:  指定该SharedPreferences的数据只能被本应用程序读、写;一般常用这个

· Context.MODE_APPEND:新内容追加到原内容后;

· Context.MODE_WORLD_READABLE:  指定 SharedPreferences数据能被其他应用程序读,但是不支持写;

· Context.MODE_WORLD_WRITEABLE:  指定 SharedPreferences数据能被其他应用程序读、写。会覆盖原数据。 

对象实例化之后可以直接使用,如果使用的话,比较简单的就是

存储数据和读取数据就只需要这两块代码。所以为啥我会在忘了之后一直对这块有误解?

//存储数据前先给edit()方法定义一下,因为edit()方法决定了写入数据这一部分。

SharedPreferences.Editor editor = prefs.edit();
editor.putInt("age", 38);
editor.putString("pwd", "123456");
editor.commit();//最后一定要commit或者apply一下。才算存进去了。



//通过键值得到数据
age_get = prefs.getInt("age",0);//这个0代表没拿到数据默认给个0上去
pwd_get = prefs.getString("pwd","");

注意上传的时候有两个方法commit()和apply() 这两个方法都可以进行上传操作。

对于 apply, listener 回调时内存已经完成同步, 但是异步磁盘任务不保证是否完成

对于 commit, listener 回调时内存和磁盘都已经同步完毕

总之意思就是apply更快,但是没有返回值,不知道是否上传成功,但是commit有返回值。在不关心提交结果是否成功的情况下,优先考虑apply方法。

 

然后稍微扯一下源码:有需要的就看一下。

getSharedPreferences是方法的开始,它就是调用了ContextImpl的getSharedPreferences()这个方法。

然后源码中声明了一个静态常量sSharedPrefs,这是个Map对象。

第一次创建的时候是进入getSharedPrefsFile(name)这个方法,这个方法里面就是makeFilename这样一个方法,也就是新建文件name.xml,在这个方法里面跟踪可以找到xml文件的存储路径。

然后就新建一个SharedPreferencesImpl()对象。因为SharedPreferences只是个接口,而真正的实现是SharedPreferencesImpl,我们后续的get,put操作实际也是通过SharedPreferencesImpl对象完成的。最后这个sSharedPrefs就全局唯一指定了这一个文件。

在SharedPreferencesImpl的构造方法中有startLoadFromDisk这个方法,可以看到从getSharedPreferences到这个方法中已经多次使用了synchronized,保证线程安全。

在这个方法中先把状态量mLoaded设为false,然后开一个新线程loadFromDiskLocked()

三个连续的if检查文件状况,然后创建BufferedInputStream,参数就是刚才创建的文件。用XmlUtils把文件中内容写入map,然后把状态量mLoaded设为true,就表示加载文件内容完成了。最后这几行可以发现,在第一次创建的时候map一定为空,这时候刚好新建mMap对象。这一整块就是加载过程了

回到getSharedPreferences方法里面,最后的

如果SharedPreferences已经存在了,会判断mode否等于Context.MODE_MULTI_PROCESS,如果API小于这个HONEYCOMB,就会进行下面的方法,仅仅是在线程安全中重新加载一次数据到mMap。

所以getSharedPreferences实际返回SharedPrefenercesImpl对象,首先在sSharedPrefs容器中查找,如果未找到则创建Sp的对象并添加到sSharedPrefs。

然后再来关注一下edit()方法的源码。这个edit其实是调用的SharedPreferencesImpl的方法

这里对于之前的状态量mLoaded会再进行一次判断。在读取文件之后这个量是true,读取文件的内容到map是在工作线程,而这个edit方法是在主线程。如果读取时间超过了5s,那么主线程就等待了5s,那么就会出现ANR(程序无响应)了。当我们第一次创建Sp对象时,它会开启一个工作线程将指定的文件中内容加载到mMap中,当加载完成改变mLoaed变量状态;否则awaitLoadedLocked方法会一直等待下去。

然后返回的EditImpl方法中就有着存储数据的put方法了,还有clear,remove等其他方法。这部分代码就比较简单,不多加说明。

我们每次调用edit都会创建一个新的EditorImpl对象,因此一般都是在最开始调用之后就不再每次调用edit方法,因为每次都会创建一个新的EditorImpl对象并且进行一次IO操作,严重影响性能。

好像是第一次看这么长的文章。

之后是commit部分,这部分通过遍历,与mMap数据做一个比较,比如相同key但是value发生了变化此时修改mMap中的数据。然后mMap就是最后一次commit的数据,然后清空容器,然后在主线程中将mMap写回到文件中。

而apply这部分就开启了一个只有一个核心线程的线程池在其中顺序进行上面的任务。这样当然更加有线程安全的保证。但是假设我们apply非常多的任务。该线程池队列是串行执行,当我们关闭Activity时,会检查队列中任务是否已经全部执行完成,否则一直等到全部执行完成。如果此时等待超过5s。。。emmm,所以一般在提交数据较多时还是使用commit更好。不过较多的数据官方也并不推荐使用SP存储。一般数据较多且必须用SP那么不如分成多个SP存储,可以加快速度并且避免ANR。

最后是get数据的时候。因为通过SharedPreferenceImpl存储的数据都会在内存中保留一份mMap,这里也是直接在mMap中读取数据即可。这也就是之前说的检查缓存中数据,没有缓存才会创建对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值