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