已广泛测试该SharedPreferences
框架。虽然大多数作品都符合人们的预期,但我遇到了一些案例,我想知道它们背后的原因是什么。我进行了一些测试,所有这些测试都通过了——它们的共同设置是:
Context ctx;
SharedPreferences prefs;
Editor ed;
protected void setUp() throws Exception {
super.setUp();
ctx = getContext();
prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
ed = prefs.edit();
}
protected void tearDown() throws Exception {
if (ed != null) ed.clear().commit();
super.tearDown();
}
现在奇怪的地方:
当我输入一个空值时,我必须使用空默认值来请求它以将其取回- 如果默认值是非空值,我将获得此默认值。即使默认值与我输入的类型不同。适用于
String
andSet<String>
(但我可以取回一个布尔值):public void testNullString() { ed.putString("string_key", null); // putString() and putStringSet() only ed.commit(); assertTrue(prefs.contains("string_key")); assertEquals(null, prefs.getString("string_key", null)); // if I specify a non null default I get this default back, not null assertEquals("a string", prefs.getString("string_key", "a string")); // *even if I ask for a boolean* assertEquals(true, prefs.getBoolean("string_key", true)); }
我可以轻松地将 a
Set<Integer>
放入 prefs :@SuppressWarnings("unchecked") @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void testNullStringSetsRaw() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { @SuppressWarnings("rawtypes") final Set integerHashSet = new HashSet(); integerHashSet.add(1); ed.putStringSet("set_key", integerHashSet); ed.commit(); final Set<String> defValues = new HashSet<String>(); defValues.add("a string"); Set<String> s = prefs.getStringSet("set_key", defValues); final String msg = "The set I put in: " + integerHashSet + " and what I got out :" + s; Log.e(TAG, msg); // the same - [1] assertTrue(msg, integerHashSet.equals(s)); assertTrue(s.contains(1)); // ! } }
空键是怎么回事?它们似乎是完全合法的钥匙:
public void testNullKeyNonNullString() { final String NULL_KEY = null; ed.putString(NULL_KEY, "a string"); ed.commit(); assertTrue("Contains null key", prefs.contains(NULL_KEY)); assertEquals("Retrieve the value giving null default", "a string", prefs.getString(NULL_KEY, null)); assertEquals("Retrieve the value giving default", "a string", prefs.getString(NULL_KEY, "a string" + "blah")); try { // no deal ! prefs.getBoolean(NULL_KEY, true); fail("Expected CCE"); } catch (ClassCastException e) {} }
但我在我的日志中看到了类似的内容:
org.xmlpull.v1.XmlPullParserException: Map value without name attribute: boolean
- 请参阅android getSharedPreferences 错误:没有名称属性的映射值:用于讨论的布尔值。我想知道这是否与null
钥匙有关。编辑:它是相关的 - 复制器:public class XmlExceptionTest extends AndroidTestCase { /** Run it twice - on the second run the exception is thrown */ public void testXmlException() { Context ctx = getContext(); SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(ctx);//exception thrown here(line 18) // and apparently it clears the prefs as the condition below is false if (prefs.contains("run_once")) { // false Log.w("XmlExceptionTest", "contains null key :" + prefs.contains(null)); } Editor e = prefs.edit(); e.putBoolean("run_once", true).commit(); e.putString(null, "I put a sting with null key").commit(); assertTrue("Contains null", prefs.contains(null)); PreferenceManager.getDefaultSharedPreferences(ctx); // exception // NOT thrown here - why ? - apparently there is a static factory // returning the instance it already constructed // e.clear().commit(); // this eliminates the exception } }
例外 :
W/ApplicationContext(): getSharedPreferences W/ApplicationContext(): org.xmlpull.v1.XmlPullParserException: Map value without name attribute: string W/ApplicationContext(): at com.android.internal.util.XmlUtils.readThisMapXml(XmlUtils.java:521) W/ApplicationContext(): at com.android.internal.util.XmlUtils.readThisValueXml(XmlUtils.java:733) W/ApplicationContext(): at com.android.internal.util.XmlUtils.readValueXml(XmlUtils.java:667) W/ApplicationContext(): at com.android.internal.util.XmlUtils.readMapXml(XmlUtils.java:470) W/ApplicationContext(): at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:361) W/ApplicationContext(): at android.preference.PreferenceManager.getDefaultSharedPreferences(PreferenceManager.java:348) W/ApplicationContext(): at gr.uoa.di.android.helpers.test.XmlExceptionTest.testXmlException(XmlExceptionTest.java:18) W/ApplicationContext(): at java.lang.reflect.Method.invokeNative(Native Method) W/ApplicationContext(): at java.lang.reflect.Method.invoke(Method.java:521) W/ApplicationContext(): at junit.framework.TestCase.runTest(TestCase.java:154) W/ApplicationContext(): at junit.framework.TestCase.runBare(TestCase.java:127) W/ApplicationContext(): at junit.framework.TestResult$1.protect(TestResult.java:106) W/ApplicationContext(): at junit.framework.TestResult.runProtected(TestResult.java:124) W/ApplicationContext(): at junit.framework.TestResult.run(TestResult.java:109) W/ApplicationContext(): at junit.framework.TestCase.run(TestCase.java:118) W/ApplicationContext(): at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169) W/ApplicationContext(): at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154) W/ApplicationContext(): at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:520) W/ApplicationContext(): at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
显然,当抛出异常时偏好被清除(坏坏坏)
所以我的问题是:确实是我所说的行为(或者我错过了一些愚蠢的事情)?它背后的动机是什么(尤其是空值行为)?它是否被记录并保证保持不变 - 或可能会改变?第2点是疏忽吗?