查看原文
其他

Android的SP存储,效率探究

2017-08-08 承香墨影 承香墨影

版权声明:

本公众号发布的所有文章,未特殊署名,均属于原创,版权归本公众号所有。

转载请参阅公众号的:《转载授权》。

一、前言

对于 SharedPreferences(以下简称SP),相信 Android 开发们,都不会陌生。无非是 Android 系统提供的一个以 Key-Value 键值对形式的存储方式。如果需要获取数据,SP 中提供了对应的getXxx() 方法,如果需要存储数据,只需要拿到 Editor 对象,在 Editor 对象中,也提供了对应的 putXxx() 方法,在操作完成之后,调用 commit() 或者 apply() 即可。
那么 commit() 和 apply() 有什么区别?就是本篇文章的主题。

二、从文档上看区别

SharedPreferences 就是一个接口,其实现类是SharedPreferencesImpl 。
关于 commit() 和 apply() 的描述,在 SharedPreferences 中,下面先看看文档中对它的说明。

从文档中可以看出一些区别:

  1. apply() 没有返回值,而 commit() 是有返回值的,返回值标识着是否执行成功。

  2. apply() 的操作是原子提交到内存中,然后以异步的方式保存到磁盘上,而 commit() 完全是以同步的方式将数据保存到磁盘上。

  3. apply() 因为没有返回值,所以不会提示任何失败。只需要调用即可。

  4. 无论是 apply() 还是 commit() ,如果同时被操作了,以最后一次操作为准。

获取SP这个对象的方式,是使用:

 Context.getSharedPreferences()

所以在同一进程中,SP 对象是以单例的形式存在的,就不需要考虑有冲突的问题。但是因为 apply() 和 commit() 的差异性,如果对提交结果不关心的话,推荐使用 apply() ,如果需要确保保存成功之后,才继续进行后续的操作,推荐使用 commit()

三、从代码中看区别

虽然从文档中,完全就可以了解清楚 SP 中,commit() 和 apply() 的具体区别和使用场景。但是作为一个有情怀的码农,还是需要再往深了一层挖挖,一探究竟。
之前提到,SharedPreferences 接口的实现类是SharedPreferencesImpl 。那么就继续看看 apply() 和 commit() 的具体实现。
apply() :

commit():

对比发现 commit() 的实现非常的简单,并且在 SP 中,是通过 enqueueDiskWrite() 方法来控制是否是异步操作的。

下面看看 enqueueDiskWrite() 方法的实现。

从注释里可以看到,如果 enqueueDiskWrite() 的第二个参数为 null 的话,则会变成同步操作。而正是因为在 commit() 中是同步操作,commit() 才可以拿到操作是否正确的结果。

具体将数据持久化到硬盘上的操作,是调用了 writeToFile() 方法,无非就是一些对文件读写的操作和 XML 的处理,这个就不再这里继续探讨了,有兴趣的可以自己看看源码。

四、从效率上看问题

看了源码更印证了之前的结论。
再从效率上看看 SP ,从 SP 提供的接口上看,get 操作应该只是去获取,这个就像从一个单例的对象中,获取一个数据一样,从效率上看应该是不存在什么损耗的。那么从存储的角度,去分析一下效率的问题。

这个先上结论,再来分析一下问题。写了一个简单的 demo :

A 操作和 B 操作,在代码逻辑上应该是一样的,都是想 SP 中写入100 次不同字段的数据,区别只是在于,A操作每次都去获取新的 Editor ,而 B 操作是只使用一个 Eidtor 去存储。两个操作都分别执行两次。

可以看出来,使用 commit() 的方式,如果每次都使用 sp.edit() 方法获取一个新的 Editor 的话,新建和修改的执行效率差了非常的大。也就是说,存储一个从来没有用过的 Key ,和修改一个已经存在的 Key,在效率上是有差别的。

然后把之前的例子中, commit() 修改成 apply() ,这里就不贴代码了。再来看看执行结果,当然在运行前需要先清空数据。这里把 A 操作和 B 操作分别执行了 4 次。

从执行结果可以发现,使用 apply() 因为是异步操作,基本上是不耗费时间的,效率上都是 OK 的。从这个结论上来看,apply() 影响效率的地方,在 sp.edit() 方法。

那么,再看看 edit() 方法是如何实现的:

可以看出来,在 edit() 中是有 synchronized 这个同步锁来保证线程安全的,纵观 EditorImpl 的实现,可以看到大部分操作都是有同步锁的,但是只锁了 (this) ,也就是只对当前对象有效,而 edit() 方法是每次都会去重新 new 一个 EditorImpl() 这个Eidtor 接口的实现类。所以效率就应该是被这里影响到了。

四、结论

既然已经分析了 SP 的文档说明和代码实现,那么就可以分析出一些SP 效率相关的结论。

  • edit() 是有效率影响的,所以不要在循环中去调用此方法,最好将 edit() 方法获取的 Editor 对象方在循环之外,在循环中共用同一个 Editor() 对象进行操作。

  • commit() 的时候,「new-key」和「update-key」的效率是有差别的,但是有返回结果。

  • apply() 是异步操作,对效率的影响,基本上是 ms 级的,可以忽略不记。



推荐阅读:





您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存