Android 高质量开发之存储优化

本文深入探讨了Android平台上数据存储的两种常见方式——SharedPreferences和ContentProvider,分析了它们在性能、安全性、稳定性和开发成本等方面的特点。SharedPreferences在跨进程安全和性能上有局限,而ContentProvider则提供了更好的跨进程数据共享,但需要关注启动性能、稳定性与安全性。此外,文章还讨论了序列化和SQLite数据库在存储优化中的角色和注意事项,为开发者选择合适的数据存储方案提供了指导。
摘要由CSDN通过智能技术生成

数据的交互及存储是任何开发都绕不过的事情,Android平台涉及数据存储有:SharedPreferences存储数据、文件存储数据、SQLite数据库存储数据、使用ContentProvider存储数据、网络存储数据

一、引言

Android 提供了很多种持久化存储的方案,存储就是把特定的数据结构转化成可以被记录和还原的格式,这个数据格式可以是二进制的,也可以是 XML、JSON、Protocol Buffer 这些格式。既然有那么多存储的方案,那我们在选择数据存储方法时,一般需要考虑哪些关键要素呢?

在选择数据存储方法时,一般会考虑下面这几要数:

  • 正确性
  • 时间开销
  • 空间开销
  • 安全
  • 开发成本
  • 兼容性

这些要素哪个最重要呢?数据存储方法不能脱离场景来考虑,任何一项目都不可能把这六个要素都做成最完美。首要考虑的是正确性,那我们可能需要采用冗余、双写等方案,那就要容忍对时间开销产生的额外影响。同样如果非常在意安全,加解密环节的开销也必不可小。如果想针对启动场景,可以选择在初始化时间和读取时间更有优势的方案。

总的来说,我们需要结合应用场景选择合适的数据存储方法。

二、SharedPreferences

SharedPreferences是 Android 中比较常用的存储方法,它可以用来存储一些比较小的键值对集合。虽然 SharedPreferences 使用非常简便,但也是我们诟病比较多的存储方法。它的性能问题比较多:

  • 跨进程不安全。由于没有使用跨进程的锁,就算使用MODE_MULTI_PROCESS,SharedPreferences 在跨进程频繁读写有可能导致数据全部丢失。根据有心人统计,SP 大约会有万分之一的损坏率。

  • 加载缓慢。SharedPreferences 文件的加载使用了异步线程,而且加载线程并没有设置线程优先级,如果这个时候主线程读取数据就需要等待文件加载线程的结束。这就导致出现主线程等待低优先级线程锁的问题,比如一个 100KB 的 SP 文件读取等待时间大约需要 50~100ms,我建议提前用异步线程预加载启动过程用到的 SP 文件。

  • 全量写入。无论是调用 commit() 还是 apply(),即使我们只改动其中的一个条目,都会把整个内容全部写到文件。而且即使我们多次写入同一个文件,SP 也没有将多次修改合并为一次,这也是性能差的重要原因之一。

  • 卡顿。由于提供了异步落盘的 apply 机制,在崩溃或者其他一些异常情况可能会导致数据丢失。所以当应用收到系统广播,或者被调用 onPause 等一些时机,系统会强制把所有的 SharedPreferences 对象数据落地到磁盘。如果没有落地完成,这时候主线程会被一直阻塞。这样非常容易造成卡顿,甚至是 ANR,从线上数据来看 SP 卡顿占比一般会超过 5%。

坦白来讲,系统提供的 SharedPreferences 的应用场景是用来存储一些非常简单、轻量的数据。我们不要使用它来存储过于复杂的数据,例如 HTML、JSON 等。而且 SharedPreference 的文件存储性能与文件大小相关,每个 SP 文件不能过大,我们不要将毫无关联的配置项保存在同一个文件中,同时考虑将频繁修改的条目单独隔离出来。

我们也可以替换通过复写 Application 的 getSharedPreferences 方法替换系统默认实现,比如优化卡顿、合并多次 apply 操作、支持跨进程操作等。具体:

public class MyApplication extends Application {
    @Override
    public SharedPreferences getSharedPreferences(String name, int mode)
    {
        return SharedPreferencesImpl.getSharedPreferences(name, mode);
    }
}

对系统提供的 SharedPreferences 的小修小补虽然性能有所提升,但是依然不能彻底解决问题。

三、ContentProvider

为什么 Android 系统不把 SharedPreferences 设计成跨进程安全的呢?那是因为 Android 系统更希望我们在这个场景选择使用 ContentProvider 作为存储方式。ContentProvider 作为 Android 四大组件中的一种,为我们提供了不同进程甚至是不同应用程序之间共享数据的机制。

Android 系统中比如相册、日历、音频、视频、通讯录等模块都提供了 ContentProvider 的访问支持。它的使用也比较简单。
当然,在使用过程也需要注意以下几点。

3.1 启动性能

ContentProvider 的生命周期默认在 Application onCreate() 之前,而且都是在主线程创建的。我们自定义的 ContentProvider 类的构造函数、静态代码块、onCreate 函数都尽量不要做耗时的操作,会拖慢启动速度。

3.2 稳定性

ContentProvider 在进行跨进程数据传递时,利用了 Android 的 Binder 和匿名共享内存机制。就是通过 Binder 传递 CursorWindow 对象内部的匿名共享内存的文件描述符。这样在跨进程传输中,结果数据并不需要跨进程传输,而是在不同进程中通过传输的匿名共享内存文件描述符来操作同一块匿

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值