打卡学习Android 性能优化—数据存储优化

本文详细介绍了Android中传统数据存储手段,特别是SharedPreference的原理和问题,如主线程阻塞、全量更新等。接着探讨了MMKV的优化方案,包括IO存储优化中的零拷贝技术、数据结构优化和多进程文件读写的安全性,揭示了MMKV如何在性能和安全性上超越SharedPreference。
摘要由CSDN通过智能技术生成

其实在Android项目开发中,数据存储是不可避免的,尤其是本地持久化存储,例如线上日志存储、本地图片存储等,尤其是线上用户行为日志存储,如果存在频繁的IO操作,会占用CPU的时间,导致手机发热,带来一些性能问题。

像在我们手机的设置当中,我们经常会设置一些选项,例如屏幕亮度、声音大小等,如果熟悉一些framework层的源码,我们会发现这些值其实就是存储在Settings的xml文件当中,如果看过设置的源码,会发现一种我们没用过的Activity - PreferenceActivity,其内部实现原理就是通过SharedPreference实现数据持久化存储。例如屏幕亮度对应一个key,对应的value值为0-255之间,每次系统重启或者修改值,都会从key中读取最新值或者覆盖之前的值,音量同理。

1 传统数据存储手段

前面我们在提到设置相关的数据变动时,是采用了sp存储,对于sp存储相信伙伴们并不陌生,在早些年对于一些轻量级的数据存储,通常都是采用sp这种手段。

1.1 SharedPreference

对于sp的用法就不过多赘述,但是对于sp的一些原理性的问题,我们需要了解。

val sp = getSharedPreferences("share", Context.MODE_PRIVATE)
sp.edit().putString("light","255").commit()

sp如何实现本地数据存储的?

其实熟悉sp的伙伴们应该都了解,当我们使用sp这个工具的时候,其实会自动帮我们生成一个xml文件,其中存储了我们定义的各种key。

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="light">255</string>
</map>

既然生成了文件,那么sp其实就是使用传统的Java IO进行文件读写,所以针对传统的IO存在的弊端,其实如果在主线程中进行读写操作,Basic IO是会阻塞主线程的,所以这就是sp存储也存在的一个弊端,频繁地使用sp存储数据,就可能会导致卡顿。

sp的数据更新是如何完成的?

前面我们提到,既然能存储数据,那么数据是随时会变的,sp是如何完成数据更新的?我们看前面提到的一个案例,就是设置屏幕亮度,当把屏幕的亮度调暗之后,只会针对sp存储中亮度这个key进行数据修改吗?其实不是的,对于sp来说,它没有增量更新的概念,因为是xml格式的数据结构,所以在更新数据的时候,会把新老数据全部序列化,然后重新覆盖原文件。

即便是文件中100个数据,只需要更新其中一条,也会全量更新,因此在提交的时候不要每次修改之后都提交。

commit和apply的区别

我们在提交修改的时候,一般是有两个方法:commit和apply。
在这里插入图片描述
我们在使用commit的时候,编译器会有提示,建议我们使用apply方法而不是commit,那么两者有什么区别呢?

If you don’t care about the return value and you’re using this from your application’s main thread, consider using apply instead. Returns: Returns true if the new values were successfully written to persistent storage.

在官网对于commit的解释是:如果开发者不关心返回值,或者说不需要使用这个返回值在主线程中进行处理,那么就可以使用apply来代替。

这句话是什么意思呢?假设我们在sp中存储了一个值,我们必须要保证这个值存储成功之后,才能执行下一步操作,例如跳转到下一级页面,而且下一级页面必须要用到这个值,那么此时就需要使用commit,因为这个是同步的操作,但是就因为这样,如果存在数据量过大的情况,可能会导致ANR。

apply commits its changes to the in-memory SharedPreferences immediately but starts an asynchronous commit to disk and you won’t be notified of any failures.

那么对于apply,官方的解释为:和commit不同的是,apply会将修改先同步提交到内存中,然后通过异步的方式将这个修改写入到磁盘文件,但是对于开发者来说,并不知道这个修改是否成功写入到磁盘缓存中。

所以针对这两种方式,开发者可以根据业务场景自行选择。

getSharedPreferences会不会阻塞主线程

我们知道,在调用getSharedPreferences方法的时候,其实会创建一个xml文件,并存储到缓存当中,当下次再次调用getSharedPreferences方法的时候,会以name当做key从缓存中获取对应的xml文件,所以我们只需要关心第一次创建的过程即可。

@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
   
    // At least one application in the world actually passes in a null
    // name.  This happened to work because when we generated the file name
    // we would stringify it to "null.xml".  Nice.
    if (mPackageInfo.getApplicationInfo().targetSdkVersion <
            Build.VERSION_CODES.KITKAT) {
   
        if (name == 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值