Android 轻量级缓存 SharedPreferences 源码解析

个人博客:https://blog.N0tExpectErr0r.cn

小专栏:https://xiaozhuanlan.com/N0tExpectErr0r

SharedPreference 是一个 Android 开发自带的适合保存轻量级数据的 K-V 存储库,它使用了 XML 的方式来存储数据,比如我就经常用它保存一些如用户登录信息等轻量级数据。那么今天就让我们来分析一下它的源码,研究一下其内部实现。

获取SharedPreferences

我们在使用 SharedPreferences 时首先是需要获取到这个 SharedPreferences 的,因此我们首先从 SharedPreferences 的获取入手,来分析其源码。

根据名称获取 SP

不论是在 Activity 中调用 getPreferences() 方法还是调用 Context 的 getSharedPreferences 方法,最终都是调用到了 ContextImpl 的 getSharedPreferences(String name, int mode) 方法。我们先看看它的代码:

@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 == null) {
   
            name = "null";
        }
    }
    File file;
    synchronized (ContextImpl.class) {
   
        if (mSharedPrefsPaths == null) {
   
            mSharedPrefsPaths = new ArrayMap<>();
        }
        file = mSharedPrefsPaths.get(name);
        if (file == null) {
   
            file = getSharedPreferencesPath(name);
            mSharedPrefsPaths.put(name, file);
        }
    }
    return getSharedPreferences(file, mode);
}

可以看到,它首先对 Android 4.4 以下的设备做了特殊处理,之后将对 mSharedPrefsPaths 的操作加了锁。mSharedPrefsPaths 的声明如下:

private ArrayMap<String, File> mSharedPrefsPaths;

可以看到它是一个以 name 为 key,name 对应的 File 为 value 的 HashMap。首先调用了 getSharedPreferencesPath 方法构建出了 name 对应的 File,将其放入 map 后再调用了 getSharedPreferences(File file, int mode) 方法。

获取 SP 名称对应的 File 对象

我们先看看是如何构建出 name 对应的 File 的。

@Override
public File getSharedPreferencesPath(String name) {
   
    return makeFilename(getPreferencesDir(), name + ".xml");
}

可以看到,调用了 makeFilename 方法来创建一个名为 name.xml 的 File。makeFilename 中仅仅是做了一些判断,之后 new 出了这个 File 对象并返回。

可以看到,SharedPreference 确实是使用 xml 来保存其中的 K-V 数据的,而具体存储的路径我们这里就不再关心了,有兴趣的可以点进去看看。

根据创建的 File 对象获取 SP

我们接着看到获取到 File 并放入 Map 后调用的 getSharedPreferences(file, mode) 方法:

@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
   
    SharedPreferencesImpl sp;
    synchronized (ContextImpl.class) {
   
        final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();	// 1
        sp = cache.get(file);
        if (sp == null) {
   
            checkMode(mode);
            if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
   
                if (isCredentialProtectedStorage()
                        && !getSystemService(UserManager.class)
                                .isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
   
                    throw new IllegalStateException("SharedPreferences in credential encrypted "
                            + "storage are not available until after user is unlocked");
                }
            }
            sp = new SharedPreferencesImpl(file, mode);	// 2
            cache.put(file, sp);
            return sp;
        }
    }
    if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
        getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
   
        // If somebody else (some other process) changed the prefs
        // file behind our back, we reload it.  This has been the
        // historical (if undocumented) behavior.
        sp.startReloadIfChangedUnexpectedly();
    }
    return sp;
}

首先可以看到注释 1 处,这里调用了 getSharedPreferencesCacheLocked 来获取到了一个 ArrayMap<File, SharedPreferencesImpl>,之后再从这个 Map 中尝试获取到对应的 SharedPreferencesImpl 实现类(简称 SPI)。

之后看到注释 2 处,当获取不到对应 SPI 时,再创建一个对应的 SPI,并将其加入这个 ArrayMap 中。

这里很明显是一个缓存机制的实现,以加快之后获取 SP 的速度,同时可以发现,SP 其实只是一个接口,而 SPI 才是其具体的实现类。

缓存机制

那么我们先来看看其缓存机制,进入 getSharedPreferencesCacheLocked 方法:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值