设为首页 加入收藏

TOP

细数 SharedPreferences 的那些槽点 !(一)
2019-09-03 03:44:48 】 浏览:47
Tags:细数 SharedPreferences 那些

前言

最近在处理一个历史遗留项目的时候饱受其害,主要表现为偶发性的 SharedPreferences 配置文件数据错乱,甚至丢失。经过排查发现是多进程的问题。项目中有两个不同进程,且会频繁的读写 SharedPreferences 文件,所以导致了数据错乱和丢失。趁此机会,精读了一遍 SharedPreferences 源码,下面就来说说 SharedPreferences 都有哪些槽点。

源码解析

SharedPreferences 的使用很简单,这里就不再演示了。下面就按 获取 SharedPreferencegetXXX() 获取数据putXXX()存储数据 这三方面来阅读源码。

1. 获取 SharedPreferences

1.1 getDefaultSharedPreferences()

一般我们会通过 PreferenceManagergetDefaultSharedPreferences() 方法来获取默认的 SharedPreferences 对象,其代码如下所示:

> PreferenceManager.java 

/**
 * 获取默认的 SharedPreferences 对象,文件名为 packageName_preferences , mode 为 MODE_PRIVATE
 */
public static SharedPreferences getDefaultSharedPreferences(Context context) {
    return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
            getDefaultSharedPreferencesMode());  // 见 1.2
}

默认的 sp 文件完整路径为 /data/data/shared_prefs/[packageName]_preferences.xmlmode 默认为 MODE_PRIVATE,其实现在也只用这种模式了,后面的源码解析中也会提到。最后都会调用到 ContextImplgetSharedPreferences() 方法。

1.2 getSharedPreferences(String name, int mode)

> ContextImpl.java

@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<>();
        }
        // 先从缓存 mSharedPrefsPaths 中查找 sp 文件是否存在
        file = mSharedPrefsPaths.get(name);
        if (file == null) { // 如果不存在,新建 sp 文件,文件名为 "name.xml"
            file = getSharedPreferencesPath(name);
            mSharedPrefsPaths.put(name, file);
        }
    }
    return getSharedPreferences(file, mode); // 见 1.3
}

首先这里出现了一个变量 mSharedPrefsPaths,找一下它的定义:

/**
 * 文件名为 key,具体文件为 value。存储所有 sp 文件
 * 由 ContextImpl.class 锁保护
 */
@GuardedBy("ContextImpl.class")
private ArrayMap<String, File> mSharedPrefsPaths;

mSharedPrefsPaths 是一个 ArrayMap ,缓存了文件名和 sp 文件的对应关系。首先会根据参数中的文件名 name 查找缓存中是否存在对应的 sp 文件。如果不存在的话,会新建名称为 [name].xml 的文件,并存入缓存 mSharedPrefsPaths 中。最后会调用另一个重载的 getSharedPreferences() 方法,参数是 File 。

1.3 getSharedPreferences(File file, int mode)

> ContextImpl.java

@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
    SharedPreferencesImpl sp;
    synchronized (ContextImpl.class) {
        final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked(); // 见 1.3.1
        sp = cache.get(file); // 先从缓存中尝试获取 sp
        if (sp == null) { // 如果获取缓存失败
            checkMode(mode); // 检查 mode,见 1.3.2
            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); // 创建 SharedPreferencesImpl,见 1.4
            cache.put(file, sp);
            return sp;
        }
    }

    // mode 为 MODE_MULTI_PROCESS 时,文件可能被其他进程修改,则重新加载
    // 显然这并不足以保证跨进程安全
    if ((mode & Context.MODE_MULTI_PROC  
		
细数 SharedPreferences 的那些槽点 !(一) https://www.cppentry.com/bencandy.php?fid=98&id=249719

首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Android四大组件之Service 下一篇亲,麻烦给个五星好评!—RatingB..