美文网首页
关于SharedPreferences的putStringSet

关于SharedPreferences的putStringSet

作者: CZKGO | 来源:发表于2020-03-11 23:00 被阅读0次

问题描述

        使用SharedPreferences的putStringSet方法之后,发现数据并没有保存,并且确定调用了apply()或commit()方法,而且putString,putInt,putLong等方法都能正常保存。

原因

        SharedPreferences接口是由SharedPreferencesImpl类实现的,其中putStringSet方法如下:

@Override
public Editor putStringSet(String key, @Nullable Set<String> values) {
    synchronized (mEditorLock) {
        mModified.put(key,
                (values == null) ? null : new HashSet<String>(values));
        return this;
    }
}

        可以看到stringSet存储在mModified变量中,然后当调用apply()或commit()方法时,最后都会调用到commitToMemory()方法,如下:

private MemoryCommitResult commitToMemory() {
    long memoryStateGeneration;
    List<String> keysModified = null;
    Set<OnSharedPreferenceChangeListener> listeners = null;
    Map<String, Object> mapToWriteToDisk;

    synchronized (SharedPreferencesImpl.this.mLock) {
        if (mDiskWritesInFlight > 0) {
            mMap = new HashMap<String, Object>(mMap);
        }
        mapToWriteToDisk = mMap;
        mDiskWritesInFlight++;

        boolean hasListeners = mListeners.size() > 0;
        if (hasListeners) {
            keysModified = new ArrayList<String>();
            listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
        }

        synchronized (mEditorLock) {
            boolean changesMade = false;

            if (mClear) {
                if (!mapToWriteToDisk.isEmpty()) {
                    changesMade = true;
                    mapToWriteToDisk.clear();
                }
                mClear = false;
            }

            for (Map.Entry<String, Object> e : mModified.entrySet()) {
                String k = e.getKey();
                Object v = e.getValue();
                if (v == this || v == null) {
                    if (!mapToWriteToDisk.containsKey(k)) {
                        continue;
                    }
                    mapToWriteToDisk.remove(k);
                } else {
                    if (mapToWriteToDisk.containsKey(k)) {
                        Object existingValue = mapToWriteToDisk.get(k);
                        //注意这里
                        if (existingValue != null && existingValue.equals(v)) {
                            continue;
                        }
                    }
                    mapToWriteToDisk.put(k, v);
                }

                changesMade = true;
                if (hasListeners) {
                    keysModified.add(k);
                }
            }

            mModified.clear();

            if (changesMade) {
                mCurrentMemoryStateGeneration++;
            }

            memoryStateGeneration = mCurrentMemoryStateGeneration;
        }
    }
    return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
            mapToWriteToDisk);
}

        可以看到在该方法中,会遍历mModified,将其中的值和mapToWriteToDisk中的相比较,当值和value都相等时,则跳出循环,进行下一次比较。mapToWriteToDisk的值来自于全局变量mMap,mMap的值在loadFromDisk()方法中加载,如下:

private void loadFromDisk() {
    //代码省略....
    try {
        stat = Os.stat(mFile.getPath());
        if (mFile.canRead()) {
            BufferedInputStream str = null;
            try {
                str = new BufferedInputStream(
                        new FileInputStream(mFile), 16 * 1024);
                map = (Map<String, Object>) XmlUtils.readMapXml(str);
            } catch (Exception e) {
                Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
            } finally {
                IoUtils.closeQuietly(str);
            }
        }
    } catch (ErrnoException e) {
        // An errno exception means the stat failed. Treat as empty/non-existing by
        // ignoring.
    } catch (Throwable t) {
        thrown = t;
    }
    //代码省略....
}

        也就是说mMap或者mapToWriteToDisk的值是之前存起来的值。之前说过putString,putInt等都可以正常存值,所以stringSet应该是在existingValue.equals(v)这句校验失败的,存的值使用了getStringSet方法返回的值,如下:

//注意:错误代码演示!!!
SharedPreferences preferences = context.getSharedPreferences(SP_FILE_NAME, Context.MODE_PRIVATE);
Set<String> set = preferences.getStringSet("test", new HashSet<String>());
SharedPreferences.Editor editor = preferences.edit();
set.add("value");
//这样的代码无效,因为就算set中的值发生了改变,但还是同一个对象
editor.putStringSet("test", set);
editor.apply();

解决方案

        不要直接使用getStringSet返回的set,需要重新new一个set,如下所示

SharedPreferences preferences = context.getSharedPreferences(SP_FILE_NAME, Context.MODE_PRIVATE);
//重新new一个set对象,将getStringSet返回的set添加进去而不是直接使用
Set<String> set = new HashSet<String>();
set.addAll(preferences.getStringSet("test", new HashSet<String>()));
SharedPreferences.Editor editor = preferences.edit();
set.add("value");
editor.putStringSet("test", set);
editor.apply();

相关文章

网友评论

      本文标题:关于SharedPreferences的putStringSet

      本文链接:https://www.haomeiwen.com/subject/fhfqjhtx.html