我可以在Android设备上使用assert吗?


Answers:


-7

该API提供了JUnit Assert

你可以做

import static junit.framework.Assert.*;

现在,您可以使用junit框架中提供的所有函数,例如assertTrue,assertEquals,assertNull。

注意不要通过eclipse导入Junit4框架,那将是org.junit软件包。您必须使用junit.framework包才能使其在android设备或仿真器上正常工作。


51
好吧,OP要求提供“ assert keyword”,这与junit.framework.Assert可以通过JIT进行优化。由于这个原因,我来到了这里。希望其他一些答案会有所帮助。
马丁

27
我讨厌粗鲁,但是这不应该被接受,因为它不能回答问题(我同意@Martin的评论)。其他答案说明了如何使assert关键字正常运行,例如,运行“ adb shell setprop debug.assert 1”
jfritz42,2012年

3
投票失败,因为OP询问了assert关键字。@scorpiodawg概述了以下过程:stackoverflow.com/a/5563637/484261

scorpiodawg的答案仅在一年后才出现,所以我猜想这个答案被接受只是因为OP由于某种原因不得不将一个答案标记为已接受。这种态度使得SO上的大量答案不完整或完全糟糕。
2014年

145

请参阅嵌入式VM控制文档(源树中的原始HTML 或格式良好的副本)。

基本上,即使.dex字节代码包含执行检查的代码,Dalvik VM仍默认设置为忽略断言检查。检查断言以两种方式之一打开:

(1)通过以下方式设置系统属性“ debug.assert”:

adb shell setprop debug.assert 1

只要您在执行此操作后重新安装应用程序,我就可以验证该文件是否可以正常工作

(2)通过将命令行参数“ --enable-assert”发送到dalvik VM,这可能不是应用程序开发人员可能能够做到的(如果我在这里错了,请有人纠正我)。

基本上,有一个标志可以全局设置,在包级别或在类级别设置,以启用相应级别的声明。该标志默认情况下处于关闭状态,因此跳过断言检查。

我在示例活动中编写了以下代码:


public class AssertActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    int x = 2 + 3;
    assert x == 4;
  }
}

对于此代码,生成的dalvik字节代码为(对于Android 2.3.3):


// Static constructor for the class
000318:                                        |[000318] com.example.asserttest.AssertActivity.:()V
000328: 1c00 0300                              |0000: const-class v0, Lcom/example/asserttest/AssertActivity; // class@0003
00032c: 6e10 0c00 0000                         |0002: invoke-virtual {v0}, Ljava/lang/Class;.desiredAssertionStatus:()Z // method@000c
000332: 0a00                                   |0005: move-result v0
000334: 3900 0600                              |0006: if-nez v0, 000c // +0006
000338: 1210                                   |0008: const/4 v0, #int 1 // #1
00033a: 6a00 0000                              |0009: sput-boolean v0, Lcom/example/asserttest/AssertActivity;.$assertionsDisabled:Z // field@0000
00033e: 0e00                                   |000b: return-void
000340: 1200                                   |000c: const/4 v0, #int 0 // #0
000342: 28fc                                   |000d: goto 0009 // -0004

: :

// onCreate() 00035c: |[00035c] com.example.asserttest.AssertActivity.onCreate:(Landroid/os/Bundle;)V 00036c: 6f20 0100 3200 |0000: invoke-super {v2, v3}, Landroid/app/Activity;.onCreate:(Landroid/os/Bundle;)V // method@0001 000372: 1501 037f |0003: const/high16 v1, #int 2130903040 // #7f03 000376: 6e20 0500 1200 |0005: invoke-virtual {v2, v1}, Lcom/example/asserttest/AssertActivity;.setContentView:(I)V // method@0005 00037c: 1250 |0008: const/4 v0, #int 5 // #5 00037e: 6301 0000 |0009: sget-boolean v1, Lcom/example/asserttest/AssertActivity;.$assertionsDisabled:Z // field@0000 000382: 3901 0b00 |000b: if-nez v1, 0016 // +000b 000386: 1251 |000d: const/4 v1, #int 5 // #5 000388: 3210 0800 |000e: if-eq v0, v1, 0016 // +0008 00038c: 2201 0c00 |0010: new-instance v1, Ljava/lang/AssertionError; // class@000c 000390: 7010 0b00 0100 |0012: invoke-direct {v1}, Ljava/lang/AssertionError;.:()V // method@000b 000396: 2701 |0015: throw v1 000398: 0e00 |0016: return-void

注意静态构造函数如何在Class对象上调用methodAssertAssertionStatus并设置类范围变量$ assertionsDisabled;。还应注意,在onCreate()中,所有引发java.lang.AssertionError的代码都已编译,但其执行取决于$ assertionsDisabled的值,该值是为静态构造函数中的Class对象设置的。

似乎主要使用JUnit的Assert类,因此使用它可能是一个安全的选择。assert关键字的灵活性是能够在开发时打开断言,然后将其关闭以发送位,而可以正常地失败。

希望这可以帮助。


看来Google已删除了可浏览的来源(我在此处看到了其他问题的答案中对此的引用)。最好的选择是获取源,或者尝试在搜索引擎中查找源。搜索“ dalvik / embedded-vm-control.html”。这是一个拥有它的地方:assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/…。希望这可以帮助。
scorpiodawg

非常有用,谢谢。但是,倒数第二段的区别使我感到困惑。我们正在讨论两种断言:第一种是JUnit Assert包的方法,例如assertNotNull(),第二种是Java语言的“ assert”关键字。您的答案对两者都适用吗?例如,如果我import static junit.framework.Assert.*然后使用它的一种方法(如)assertNotNull("It's null!", someObject);,则该声明是否在装运位中关闭了?
杰夫罗

1
杰夫罗,您好,我不相信-junit.framework.Assert只是一个类,当发现输入条件为假时,它将引发异常。另一方面,assert关键字已内置到该语言中。希望这可以帮助。
scorpiodawg 2011年

3
adb shell setprop debug.assert 1在Eclipse中有办法吗?
Pacerier

1
如果您是root用户,也可以从设备上运行的终端进行断言。首先su,然后setprop debug.assert 1。请注意,您显示的反汇编代码将保留在发布版本中(stackoverflow.com/a/5590378/506073)。我不相信可以告诉Javac编译器不要发出断言,因此需要以某种方式将其删除。一个简单的解决方案是将关键字assert包裹在您自己的函数中,proguard可以为您剥离。
ahcox

10

启用断言后,assert关键字仅AssertionError在布尔表达式为时抛出false

因此,IMO,最好的选择,尤其是。如果您不愿意依赖junit,则要AssertionError显式地抛出如下图所示:

assert x == 0 : "x = " + x;

上述声明的替代方法是:

Utils._assert(x == 0, "x = " + x);

该方法定义为:

public static void _assert(boolean condition, String message) {
    if (!condition) {
        throw new AssertionError(message);
    }
}

Oracle Java文档建议AssertionError替换为可接受的替代方法。

我猜您可以配置Proguard去除对生产代码的调用。


但是如何启用断言?在Android Studio上?
SMBiggs '16

8

在“ Android实践”中,建议使用:

$adb shell setprop dalvik.vm.enableassertions all

如果此设置未在手机上保留,则可以使用以下属性创建/data/local.prop文件:

dalvik.vm.enableassertions=all

根据stackoverflow.com/a/18556839/2004714,您可能还需要确保将文件设置为只读(chmod 644)。
Paulo

5

直到我在google上检查了问题,我的断言才起作用,这使我烦恼了……我放弃了简单的断言,将使用Junits断言方法。

为了方便起见,我使用:

导入静态junit.framework.Assert。*;

由于静态导入,我以后可以写:

assertTrue(...); 而不是Assert.assertTrue(...);


4

如果您担心带有JUnit断言的代码(或其他任何类路径)的运输,可以使用ProGuard config选项“ assumenosideeffects”,它会假定删除该类路径对代码没有任何作用,从而去除了一个类路径。 。

例如。

-assumenosideeffects junit.framework.Assert {
*;
}

我有一个通用的调试库,将所有测试方法都放入了该库中,然后使用此选项将其从发布的应用程序中剥离。

这也消除了很难发现的问题,这些问题是从未在发行代码中使用过的被操纵的字符串。例如,如果您编写一个调试日志方法,并且在该方法中记录日志字符串之前检查调试模式,那么您仍在构造字符串,分配内存,调用该方法,但随后却什么也不做。剥离该类,然后完全删除调用,这意味着只要在方法调用中构造了您的字符串,它也会消失。

但是,请确保只删除线段是真正安全的,因为这样做无需检查ProGuard的零件。删除任何void返回方法都可以,但是,如果要从要删除的内容中获取任何返回值,请确保没有将它们用于实际的操作逻辑。


1
我认为正确的语法应为:-assumenosideeffects class junit.framework.Assert { *; }
Pooks 2012年

令人困惑的答案。提及的命令会导致proguard错误。Pooks纠正的命令仍然不能从二进制dex文件中删除断言。
指针为空

我有同样的问题@PointerNull。断言不会删除。
Mahdi

3

您可以使用断言,但是要可靠地使用它们需要一些工作。系统属性debug.assert不可靠;看问题175697651833678617324

一种方法是将每个assert语句转换为任何运行时都可以处理的内容。使用Java编译器前面的源预处理器执行此操作。例如,使用以下语句:

assert x == 0: "Failure message";

对于调试版本,预处理器会将以上内容转换为if语句:

{ if( !(x == 0) ) throw new AssertionError( "Failure message" ); }

对于生产版本,为空语句:

;

注意,这将在运行时控制声明,而不是运行时(通常的做法)。

我找不到现成的预处理器,因此编写了一个脚本。请参阅处理断言的部分。复制许可在这里



0

使用标准的Java assert关键字,例如:

assert a==b;

为此,您必须在/system/build.prop中添加一行,然后重新启动电话:

debug.assert=1

这将在植根电话上工作。使用一些能够编辑build.prop的文件管理器(例如X-plore)。

优点:大多数(全部?)Android手机出厂时都禁用了断言。即使您的代码意外断言为false,应用也不会中断或崩溃。但是,在开发设备上,您将获得断言异常。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.