Android的共享preferences null键,数值和集合<> - 个别案例数值、个别、案例、preferences

2023-09-08 09:08:35 作者:比起失望我宁愿孤独

得到了广泛的测试共享preferences 框架。虽然大多数的作品作为一个希望我碰到一些情况下,我不知道什么是他们背后的推理运行。我给一些测试所有这些都通过 - 他们共同设立是:

 上下文CTX;
共享preferences preFS;
编辑编;
保护无效设置()抛出异常{
    super.setUp();
    CTX =的getContext();
    preFS = preferenceManager.getDefaultShared preferences(CTX);
    ED = prefs.edit();
}
保护无效tearDown的()抛出异常{
    如果(ED!= NULL)ed.clear()提交()。
    super.tearDown();
}
 

现在怪异的要点:

当我把一个空值,我要问它有一个空的默认把它找回来的 - 如果默认为非null我得到这个默认了。 即使默认为不同类型从一个我放在的的。适用于字符串设置<字符串> (但我可以得到一个布尔值为例):

 公共无效testNullString(){
    ed.putString(string_key,NULL); // putString()和putStringSet()只
    ed.commit();
    assertTrue(prefs.contains(string_key));
    的assertEquals(空,prefs.getString(string_key,NULL));
    //如果我指定一个非空默认我得到这个默认回,不为空
    的assertEquals(字符串,prefs.getString(string_key,串));
    // *即使我问一个布尔值*
    的assertEquals(真,prefs.getBoolean(string_key,真));
}
 
Android Preferences的使用

我可以很容易地把一个设置<整数GT; 的preFS里面:

  @燮pressWarnings(未登记)
@TargetApi(Build.VERSION_ codeS.HONEYCOMB)
公共无效testNullStringSetsRaw(){
    如果(Build.VERSION.SDK_INT> = Build.VERSION_ codeS.HONEYCOMB){
        @燮pressWarnings(rawtypes)
        决胜盘integerHashSet =新的HashSet();
        integerHashSet.add(1);
        ed.putStringSet(set_key,integerHashSet);
        ed.commit();
        决胜盘<字符串> defValues​​ =新的HashSet<字符串>();
        defValues​​.add(字符串);
        设置<字符串> S = prefs.getStringSet(set_key,defValues​​);
        最后弦乐味精=设定的我把+ integerHashSet
            +和我下了车:+ S;
        Log.e(TAG,味精); //相同 -  [1]
        assertTrue(味精,integerHashSet.equals(S));
        assertTrue(s.contains(1)); //!
    }
}
 

什么是处理null键?他们似乎是完全合法的键:

 公共无效testNullKeyNonNullString(){
    最后弦乐NULL_KEY = NULL;
    ed.putString(NULL_KEY,串);
    ed.commit();
    assertTrue(包含null键,prefs.contains(NULL_KEY));
    的assertEquals(检索值给空默认,串,
        prefs.getString(NULL_KEY,NULL));
    的assertEquals(检索值给予默认,串,
        prefs.getString(NULL_KEY,串+嗒嗒));
    尝试 {
        // 没有成交 !
        prefs.getBoolean(NULL_KEY,真正的);
        失败(预期CCE);
    }赶上(ClassCastException异常E){}
}
 

但我已经看到我的日志之类的东西: org.xmlpull.v1.XmlPullParserException:地图值,而name属性:布尔 - 见android getShared preferences错误:地图值,而name属性:布尔了讨论。我不知道这是关系到键。 修改:这是所关乎再生器:

 公共类XmlExceptionTest扩展AndroidTestCase {

    / **运行了两次 - 在第二次运行抛出异常* /
    公共无效testXmlException(){
        上下文CTX =的getContext();
        共享preferences preFS = preferenceManager
            .getDefaultShared preferences(CTX); //这里异常抛出(第18行)
        //显然它清除preFS为下面的条件为假
        如果(prefs.contains(run_once)){//假
            Log.w(XmlExceptionTest
                包含null键+ prefs.contains(空));
        }
        编辑E = prefs.edit();
        e.putBoolean(run_once,真).commit();
        。e.putString(NULL,我把与空密钥刺)提交();
        assertTrue(包含空值,prefs.contains(空));
        preferenceManager.getDefaultShared preferences(CTX); // 例外
        //没有抛出在这里 - 为什么? - 显然有一个静态工厂
        //返回它已经构建的实例
        // e.clear()提交()。 //这消除了异常
    }
}
 

异常:

  W / ApplicationContext的():getShared preferences
W / ApplicationContext中():org.xmlpull.v1.XmlPullParserException:没有name属性地图值:字符串
W / ApplicationContext中():在com.android.internal.util.XmlUtils.readThisMapXml(XmlUtils.java:521)
W / ApplicationContext中():在com.android.internal.util.XmlUtils.readThisValueXml(XmlUtils.java:733)
W / ApplicationContext中():在com.android.internal.util.XmlUtils.readValueXml(XmlUtils.java:667)
W / ApplicationContext中():在com.android.internal.util.XmlUtils.readMapXml(XmlUtils.java:470)
W / ApplicationContext中():在android.app.ContextImpl.getShared preferences(ContextImpl.java:361)
W / ApplicationContext中():在安卓preference preferenceManager.getDefaultShared preferences(preferenceManager.java:348)。
W / ApplicationContext中():在gr.uoa.di.android.helpers.test.XmlExceptionTest.testXmlException(XmlExceptionTest.java:18)
W / ApplicationContext中():在java.lang.reflect.Method.invokeNative(本机方法)
W / ApplicationContext中():在java.lang.reflect.Method.invoke(Method.java:521)
W / ApplicationContext中():在junit.framework.TestCase.runTest(TestCase.java:154)
W / ApplicationContext中():在junit.framework.TestCase.runBare(TestCase.java:127)
W / ApplicationContext中():在junit.framework.TestResult $ 1.protect(TestResult.java:106)
W / ApplicationContext中():在junit.framework.TestResult.runProtected(TestResult.java:124)
W / ApplicationContext中():在junit.framework.TestResult.run(TestResult.java:109)
W / ApplicationContext中():在junit.framework.TestCase.run(TestCase.java:118)
W / ApplicationContext中():在android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
W / ApplicationContext中():在android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
W / ApplicationContext中():在android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:520)
W / ApplicationContext中():在android.app.Instrumentation $ InstrumentationThread.run(Instrumentation.java:1447)
 

和明显的的preferences的时候,抛出异常清零的(坏的坏人坏事)

所以,我的问题是:确实是行为,我的状态是(或我错过了一些愚蠢的)?这是什么背后的动机(尤其是空值行为)?难道记录,并保证保持如此 - 或者可能改变?是第2点的监督?

测试项目。

解决方案

这是在code

这就是我不理解泛型:

  

您可以   很容易地把一个字符串转换为的HashSet<整数GT; 通过使用原始类型的HashSet

在这里发布一个问题: HTTP:/ /$c$c.google.com/p/android/issues/detail?id=63463 (开始的讨论在谷歌集团与通常NOOP结果)。事实上,作为2014年1月7日发行标记为未来的版本:)

测试项目。

Been extensively testing the SharedPreferences framework. While most works as one would expect I run across some cases where I wonder what's the reasoning behind them. I give some tests all of which pass - their common set up is :

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();
}

Now the weird points :

When I put a null value in, I have to ask for it with a null default to get it back - if the default is non null I get this default back. Even if the default is of a different type from the one I put in. Applies to String and Set<String> (but I can get back a boolean for instance) :

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));
}

I can easily put a Set<Integer> inside the 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)); // !
    }
}

What's the deal with null keys ? They seem to be perfectly legal keys:

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) {}
}

but I have seen in my logs things like : org.xmlpull.v1.XmlPullParserException: Map value without name attribute: boolean - see android getSharedPreferences error: Map value without name attribute: boolean for a discussion. I wonder if this is related to null keys. EDIT : it is related- reproducer :

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
    }
}

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)

and apparently the preferences are cleared when the exception is thrown (bad bad bad)

So my questions are : is indeed the behavior as I state it (or have I missed something silly) ? What is the motivation behind it (especially the null values behavior) ? Is it documented and guaranteed to stay so - or may change ? Is point 2 an oversight ?

Test project.

解决方案

It's in the code

It's me not understanding generics:

You can easily put a String into a HashSet<Integer> by using the raw type HashSet

Posted an issue here: http://code.google.com/p/android/issues/detail?id=63463 (after starting a discussion in google groups with the usual noop results). Actually as of 2014.01.07 the issue is marked Future Release :)

Test project.

 
精彩推荐