2

已广泛测试该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();
}

现在奇怪的地方:

  1. 当我输入一个空值时,我必须使用空默认值来请求它以将其取回- 如果默认值是非空值,我将获得此默认值。即使默认值与我输入的类型不同。适用于Stringand Set<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));
    }
    
  2. 我可以轻松地将 aSet<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)); // !
        }
    }
    
  3. 空键是怎么回事?它们似乎是完全合法的钥匙:

    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点是疏忽吗?

测试项目。

4

1 回答 1

2
  1. 在代码中

  2. 是我不理解泛型:

    HashSet<Integer>您可以使用原始类型 轻松地将 String 放入 a HashSet

  3. 在这里发布了一个问题:http ://code.google.com/p/android/issues/detail?id=63463 (在谷歌小组开始讨论后,通常会得到 noop 结果)。实际上,截至 2014.01.07,该问题被标记为 Future Release :)

测试项目。

于 2013-12-25T06:20:51.420 回答