为什么对具有默认值报告的SharedPreferences.getString()进行代码检查“可能产生NPE”?

时间:2015-07-14 10:38:08

标签: android android-studio android-preferences

使用以下代码的默认设置在Android Studio 1.2.2中运行“分析/检查代码”时:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        String value = sharedPref.getString("somekey", "default");
        if (value.equals("default")) {
            Log.d("MainActivity", "value matches default");
        }
    }
}

它在“可能的错误”下产生以下警告:

  

第16行的方法调用'value.equals(“default”)'可能会产生'java.lang.NullPointerException'

但是,如果首选项不存在,则返回默认值。即使使用以下命令将首选项的值显式设置为null

sharedPref.edit().putString("somekey", null).commit();

将返回默认值(可能在引擎盖下这实际上是删除了首选项,但这在这里并不重要)。

上述代码实际生成NPE的唯一方法是在首选项不存在时传递null值作为默认值:

String value = sharedPref.getString("somekey", null);

然而,如果某人这样做了,那么他之后很可能错过了空检查。我确实认识到null在这里可能不是文字,可能来自其他一些变量,但同样也是一个不太可能的用例来获取偏好。

我确实意识到“这只是一个警告”,我“可以忽略它”而且这是我的代码,我可以做“我想要的任何东西”和“棉绒不是保姆”以及其他所有内容,但是它让我感到烦恼(请原谅双关语),在代码检查过程中,这个显然微不足道的非bug会显示为“可能的错误”。

现在问题:

  1. 在我推测的无错误代码中是否存在一些潜伏的危险,我还没有看到过?
  2. 代码检查是否不够智能?
  3. 在代码检查期间实际触发此警告的内容是什么?
  4. 如何删除警告?
  5. 试图回答4.我想出了以下内容:

    1. 什么都没有,只是忽略输出。看起来很丑陋,看起来你根本没有考虑警告。
    2. 禁用整个警告类。可能隐藏其他真正的错误。
    3. 经典,将等于约为"default".equals(value)。不适用于switch(value){...}
    4. 只需添加一个单独的空检查if (value != null) {...}。多余的检查,投降的气味。
    5. 试图回答3.我已经尝试了一下:

      public class MainActivity extends Activity {
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              String value = null;
              if (value.equals("default")) {
                  Log.d("MainActivity", "value matches default");
              }
          }
      }
      

      导致预期警告并按预期爆炸。很公平。

      下一步...

      public class MainActivity extends Activity {
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              String value = "test";
              if (value.equals("default")) {
                  Log.d("MainActivity", "value matches default");
              }
          }
      }
      

      没有警告,没有崩溃。明显。

      下一步...

      public class MainActivity extends Activity {
          private String getString() {
              String value = null;
              if (new Random().nextBoolean())
                  value = "test";
              return value;
          }
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              String value = getString();
              if (value.equals("default")) {
                  Log.d("MainActivity", "value matches default");
              }
          }
      }
      

      显然随机崩溃(字面意思),但没有警告!哎哟。

      所以,让我们在@Nullable上贴上getString()注释。现在我们得到警告,好。但是,SharedPreferences getString()上似乎没有注释。

      好吧,让我们尝试相反的方式。在@NonNull上粘贴getString()。新警告:

        

      表达式'value'可能会计算为null,但由声明为@NotNull的方法返回(在第28行)

      很酷,所以代码检查知道getString()的结果可能是null,但为什么在尝试使用返回值时它不会给我们原始警告?

      更新

      这里还有一个隐含的问题,第一次我可能没有强调过。

      此特定代码检查的描述如下:

        

      标记为@Nullable或@NotNull的变量,方法参数和返回值被视为可为空(或分别为非null)并使用   在分析期间检查可空性合同,例如报告可能的NullPointerException错误。

      这似乎意味着那些注释是驱动警告的原因。

      实际上,我的方法在使用@Nullable注释时会生成警告 。但是,SharedPreferences getString()会生成此警告,但已注释。这是为什么?

1 个答案:

答案 0 :(得分:2)

  

在我推测的无错误代码中是否存在一些我尚未见过的潜伏危险?

可能不是。注意分析仪结果并通过修复或有选择地抑制它们来解决任何问题仍然是有意义的。

  

代码检查是否不够智能?   在代码检查期间实际触发此警告的是什么?

是的,它不够聪明。

分析器可以看到一个方法可以返回null并且返回值是未经检查的,但不能足够深入地说明你的代码中不会出现这种情况。

  

如何删除警告?

体型

"default".equals(value)
正如您已经发现的那样,

样式。这样可以在所有情况下进行无NPE比较,并且还可以防止分析仪唠叨。

如果不可能,请取消警告本地,例如:

//noinspection ConstantConditions    
switch (value) {
    ...