设为首页 加入收藏

TOP

细数 SharedPreferences 的那些槽点 !(二)
2019-09-03 03:44:48 】 浏览:185
Tags:细数 SharedPreferences 那些
ESS) != 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; }

SharedPreferences 只是接口而已,我们要获取的实际上是它的实现类 SharedPreferencesImpl 。通过 getSharedPreferencesCacheLocked() 方法可以获取已经缓存的 SharedPreferencesImpl 对象和其 sp 文件。

1.3.1 getSharedPreferencesCacheLocked()
> ContextImpl.java

private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
    if (sSharedPrefsCache == null) {
        sSharedPrefsCache = new ArrayMap<>();
    }

    final String packageName = getPackageName();
    ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName);
    if (packagePrefs == null) {
        packagePrefs = new ArrayMap<>();
        sSharedPrefsCache.put(packageName, packagePrefs);
    }

    return packagePrefs;
}

sSharedPrefsCache 是一个嵌套的 ArrayMap,其定义如下:

private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;

以包名为 key ,以一个存储了 sp 文件及其 SharedPreferencesImp 对象的 ArrayMap 为 value。如果存在直接返回,反之创建一个新的 ArrayMap 作为值并存入缓存。

1.3.2 checkMode()
> ContextImpl.java

private void checkMode(int mode) {
    // 从 N 开始,如果使用 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE,直接抛出异常
    if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.N) {
        if ((mode & MODE_WORLD_READABLE) != 0) {
            throw new SecurityException("MODE_WORLD_READABLE no longer supported");
        }
        if ((mode & MODE_WORLD_WRITEABLE) != 0) {
            throw new SecurityException("MODE_WORLD_WRITEABLE no longer supported");
        }
    }
}

从 Android N 开始,明确不再支持 MODE_WORLD_READABLEMODE_WORLD_WRITEABLE,再加上 MODE_MULTI_PROCESS 并不能保证线程安全,一般就使用 MODE_PRIVATE 就可以了。

1.4 SharedPreferencesImpl

如果缓存中没有对应的 SharedPreferencesImpl 对象,就得自己创建了。看一下它的构造函数:

SharedPreferencesImpl(File file, int mode) {
    mFile = file; // sp 文件
    mBackupFile = makeBackupFile(file); // 创建备份文件
    mMode = mode; 
    mLoaded = false; // 标识 sp 文件是否已经加载到内存
    mMap = null; // 存储 sp 文件中的键值对
    mThrowable = null;
    startLoadFromDisk(); // 加载数据,见 1.4.1
}

注意这里的 mMap,它是一个 Map<String, Object>,存储了 sp 文件中的所有键值对。所以 SharedPreferences 文件的所有数据都是存在于内存中的,既然存在于内存中,就注定它不适合存储大量数据。

1.4.1 startLoadFromDisk()
> SharedPreferencesImpl.java

private void startLoadFromDisk() {
    synchronized (mLock) {
        mLoaded = false;
    }
    new Thread("SharedPreferencesImpl-load") {
        public void run() {
            loadFromDisk(); // 异步加载。 见 1.4.2
        }
    }.start();
}
1.4.2 loadFromDisk()
> SharedPreferencesImpl.java

private void loadFromDisk() {
    synchronized (mLock) { // 获取 mLock 锁
        if (mLoaded) { // 已经加载进内存,直接返回,不再读取文件
            return;
        }
        if (mBackupFile.exists()) { // 如果存在备份文件,直接将备份文件重命名为 sp 文件
            mFile.delete();
            mBackupFile.renameTo(mFile);
        }
    }

    // Debugging
    if (mFile.exists() && !mFile.canRead()) {
        Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
    }

    Map<String, Object> map = null;
    StructStat stat = null;
    Throwable thrown = null;
    try { // 读取 sp 文件
        stat = Os.stat(mFile.getPath());
        if (mFile.canRead()) {
            BufferedInputStream str = null;
            try {
                str = new BufferedInputStream(
                        new FileInputStream(mFile), 16 * 1024);
                map = (Map<Stri
首页 上一页 1 2 3 4 5 6 7 下一页 尾页 2/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Android四大组件之Service 下一篇亲,麻烦给个五星好评!—RatingB..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目