危险的SharedPreference操做

    Android开发中常常用到SharedPreference来存储一些配置数据。它的API比较简单,易用。可是在一次简单的bug排查中发现了一个很是有意思的问题。如今就把这个问题和分析结果写下来,仅供参考。 java

    咱们的一个模块在SharedPreference中写了一条数据,结果发现重启手机以后,SharedPreference中全部的数据都丢失了。 android

    这个问题很严重,沿着逻辑分支走了几遍,不像是误删除的结果。回过头来仔细看了一下最后一次SharedPreference中写入的数据,才轰然大悟。 app

<map>
<boolean value="true">
</map>

    问题出在:写入数据的key是空,只有value是true。这里有两个问题。第一为何key为空。第二为何会致使SharedPreference全部的数据丢失? 函数

    第一个问题是产品逻辑的bug。 google

    第二个问题的分析以下: spa

    1)SharedPreference的实现类是android.app.SharedPreferencesImpl。当该类初始化的时候,会从XML文件中把保存的内容加载的内存中。 code

private void loadFromDiskLocked() {
        ..........
        Map map = null;
        FileStatus stat = new FileStatus();
        if (FileUtils.getFileStatus(mFile.getPath(), stat) && mFile.canRead()) {
            try {
                BufferedInputStream str = new BufferedInputStream(
                        new FileInputStream(mFile), 16*1024);
                map = XmlUtils.readMapXml(str);
                str.close();
            } catch (XmlPullParserException e) {
                Log.w(TAG, "getSharedPreferences", e);
            } catch (FileNotFoundException e) {
                Log.w(TAG, "getSharedPreferences", e);
            } catch (IOException e) {
                Log.w(TAG, "getSharedPreferences", e);
            }
        }
        mLoaded = true;
        if (map != null) {
            mMap = map;
            mStatTimestamp = stat.mtime;
            mStatSize = stat.size;
        } else {
            mMap = new HashMap<String, Object>();
        }
        notifyAll();
    }

    2) XmlUtils.readMapXml(str)中发生了什么事情? 最后追踪到这样一个函数: xml

public static final HashMap readThisMapXml(XmlPullParser parser, String endTag, String[] name)
            throws XmlPullParserException, java.io.IOException
    {
        HashMap map = new HashMap();

        int eventType = parser.getEventType();
        do {
            if (eventType == parser.START_TAG) {
                Object val = readThisValueXml(parser, name);
                if (name[0] != null) {
                    //System.out.println("Adding to map: " + name + " -> " + val);
                    map.put(name[0], val);
                } else {
                    throw new XmlPullParserException(
                            "Map value without name attribute: " + parser.getName());
                }
            } else if (eventType == parser.END_TAG) {
                if (parser.getName().equals(endTag)) {
                    return map;
                }
                throw new XmlPullParserException(
                        "Expected " + endTag + " end tag at: " + parser.getName());
            }
            eventType = parser.next();
        } while (eventType != parser.END_DOCUMENT);

        throw new XmlPullParserException(
                "Document ended before " + endTag + " end tag");
    }

    看到这里就清楚了,由于最开始的那个XML文件中boolean只有value,没有name,致使name[0] == null,抛出异常。结果整个返回的HashMap是空。 内存

    Google在Issue 63463中fix了这个问题: 开发

    http://code.google.com/p/android/issues/detail?id=63463

    简单的讲就是:SharedPreference在读/写的时候支持null key,而再也不是抛异常了。

    相关的讨论在这里:

    https://groups.google.com/forum/#!topic/android-developers/JpDzZtbrjfA

相关文章
相关标签/搜索