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