处理Android Studio的NullPointerException lint警告的正确方法

35

我刚接触安卓/Java编程,不确定如何正确处理这个警告。

方法调用 '' 可能会导致 'Java.lang.NullPointerException'

输入图像描述

我应该使用断言(assert)来消除警告吗? 输入图像描述

还是使用运行时异常(runtime exception)? 输入图像描述

感谢任何帮助。


如果您首先检查对象是否为空,则警告将消失。但是,如果您确定某些内容永远不会为空,则最终会出现大量不必要的空值检查。 - tyczj
我应该禁用这个 Lint 警告吗? - Hackmodford
我不会这样做,因为它可能会揭示你可能忽略的问题,但当我知道某些东西不会真正为空时,我个人会忽略它。 - tyczj
4
对于警告信息我有强迫症问题 :P - Hackmodford
8个回答

30
我怀疑这个问题不能得出确定的答案,因为这是一个观点问题。或者至少我认为是这样 - 也是一种观点。 :)
我理解您想要“0警告”(非常值得称赞的目标),但可能没有“一刀切”的解决方案。话虽如此...
我认为您不应该做以下事情:
- 使用assert。虽然您可以添加assert语句,但Dalvik会忽略它们。如果您想要,可以配置模拟器使用它们,但真实设备不行(请参见Can I use assert on Android devices?)。因此,尽管它可能会消除警告,但在实践中是无用的。 - 使方法抛出NullPointerException。一般来说,这是一个坏主意。在这种情况下,由于您可能正在覆盖onOptionsItemSelected(),因此甚至不可能。 检查(variable != null)通常是最好的方法。如果确实是这样,则还有其他选项。
  • 如果这是一个你可以从中恢复的问题,即使searchView不在那里,你仍然可以继续应用程序,请这样做。例如,只需从方法返回。但最好记录此情况,以便在测试时能够发现它。
  • 否则,如果无法继续,则抛出异常。你想要尽早失败,以便问题可以轻松检测到。对于这种情况,合理的异常是IllegalStateException(请参见Java等效于.NET System.InvalidOperationException)。它基本上表示在不适当的时间执行了该方法。但是要小心,作为RuntimeException,这些异常是未经检查的,因此可能会导致应用程序崩溃。

你自己的链接回答说明在真实设备上启用assert关键字是可能的(尽管显然只能通过ADB)。 - Thorn G
@TomG 据我所知,setprop 只能在已经 root 的设备上使用。但是也有可能我错了? - matiash
哦,你可能是对的——我只在已经取得了 root 权限的 kiosk 设备上使用过它。算了! - Thorn G
1
绝大多数这些警告都是在findViewById()调用之后出现的,这种情况可能会发生很多次!作为Android程序员,我们习惯于从中获得空指针错误,并在更改布局文件时使用它们进行调试。不得不测试每一个这样的调用真的非常痛苦。这个新的“特性”(旧版本的Android Studio没有这个警告)极大地增加了代码膨胀。 - SMBiggs
@ScottBiggs 幸运的是,现在你可以禁用某些方法的警告。请参见我下面的答案。 - Iwo Banas

28

我开始在确定id不为空的简单方法上使用 @SuppressWarnings("ConstantConditions")


2
很好,这正是我们想要的。仅在我们确定ID不可能为空的方法上禁用检查。 - Adrien Dos Reis
或者在你想要空指针错误的地方使用它(比如在“onCreate”中)。 - SMBiggs

8

@Herrbert74 建议是可行的,但有时候最好不要在整个方法中添加 @SuppressWarnings("ConstantConditions")。更好的做法是在被警告的代码行上使用 //noinspection ConstantConditions

以下是我的经验之谈:

  • 当方法简单时,请使用 @SuppressWarnings("ConstantConditions")

  • 当方法复杂且需要仅消除特定行的警告时,请使用 //noinspection ConstantConditions


非常优雅的解决方案。+1 - andreikashin
我的个人观点是 //noinspection ConstantConditions 更好。 - Chaitanya Karmarkar

1

正如@matiash所指出的,没有一种适合所有情况的解决方案。

对我来说,一个好的折中方案是禁用所有对findViewById()方法的调用的NullPointerException警告,并保留其他方法调用的警告。这样,我就承担了检查资源id的责任,但仍然可以在犯其他错误时受益于警告。

为了实现这一点,我使用了Android Studio快速修复菜单添加了_ -> !null方法合同。

该操作在我的项目根目录下的android/support/v7/app/annotations.xml文件中生成了以下文件。

<root>
  <item name='android.support.v7.app.AppCompatActivity android.view.View findViewById(int)'>
    <annotation name='org.jetbrains.annotations.Contract'>
      <val val="&quot;_ -&gt; !null&quot;" />
    </annotation>
  </item>
</root>

更新: 不幸的是,它无法在Android Studio重启后保留 :-( 外部注释非常有用,所以我希望能找到一种方法,在重启后让Android Studio加载它们。

1

是的。在验证时使用 if (Object != null){} 是正确的方式。try {} catch (NullPointerException) {} 是下一个解决方案,这种情况下更受青睐。

如果你想要摆脱它,请抛出一个 NullPointerException。在这种情况下,Lint 将会忽略它。public void myFunc() throws NullPointerException{}

无论如何,良好的编码总是意味着在运行时验证可能存在的问题。验证!= null就可以了,在可能为null的情况下应该始终使用。


1

我喜欢这个链接的回答。

警告不是错误。而你所谈论的那个警告说“它可能会产生”,而不是“它必须产生”。所以选择权在你手中。可以选择添加空指针检查,也可以选择不添加。

所以,如果你确定你的代码中的findViewById永远不会导致NPE,则不需要添加空指针检查。


1
我个人更喜欢使用try{ }catch{ },因为它更加优雅。但是,如果你想象一下将每个可能的NULL值放入try catch中(如果它们不相邻),它确实会使你的代码变得臃肿。

0

我使用了Objects.requireNonNull(),在我看来这是一种不错的方式。正如@matiash所提到的,在每种情况下都不是一种万无一失的方式,但是在你确定数据不会为null的情况下,你可以使用这种方法来摆脱警告。如果由于某些未知原因它确实失败了,那么你将会得到NullPointerException,而即使不使用这个,你也会得到。

// before
cal.setTime(date);

// after
cal.setTime(Objects.requireNonNull(date));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接