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

时间:2010-03-02 16:45:40

标签: android assert

我想在我的Android应用中使用 Assert 关键字在某些情况下在模拟器或测试期间的设备上销毁我的应用。这可能吗?

似乎模拟器只是忽略了我的断言。

9 个答案:

答案 0 :(得分:145)

请参阅嵌入式VM控制文档(来自source tree的原始HTML或nicely formatted副本)。

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

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

adb shell setprop debug.assert 1
只要您在执行此操作后重新安装应用程序或

,我验证的工作是按预期进行的

(2)通过向dalvik VM发送命令行参数“--enable-assert”,这可能不是app开发人员可能做的事情(如果我在这里错了,有人会纠正我)。< / p>

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

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


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对象上调用expectAssertionStatus方法并设置类范围变量$ assertionsDisabled;另请注意,在onCreate()中,所有抛出java.lang.AssertionError的代码都是编译进来的,但是它的执行取决于为静态构造函数中的Class对象设置的$ assertionsDisabled的值。

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

希望这有帮助。

答案 1 :(得分:10)

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

所以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文档recommendAssertionError作为可接受的替代方案。

我猜你可以配置Proguard去掉这些生产代码的调用。

答案 2 :(得分:8)

在“Android in Practice”中,建议使用:

$adb shell setprop dalvik.vm.enableassertions all

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

dalvik.vm.enableassertions=all

答案 3 :(得分:5)

这让我大吃一惊,我的断言没有用,直到我在google上检查了这个问题...我放弃了简单的断言,并将使用junits断言方法。

为方便起见,我正在使用:

import static junit.framework.Assert。*;

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

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

答案 4 :(得分:4)

如果您担心在(或任何其他类路径)中使用JUnit断言传送代码,您可以使用ProGuard配置选项'assumenosideeffects',这将删除类路径,假设删除它什么都不做代码。

EG。

-assumenosideeffects junit.framework.Assert {
*;
}

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

这也消除了从未在发布代码中使用过的操作字符串的难以发现的问题。例如,如果您编写调试日志方法,并且在该方法中,在记录字符串之前检查调试模式,您仍在构造字符串,分配内存,调用方法,但随后选择不执行任何操作。剥离类然后完全删除调用,这意味着只要你的字符串在方法调用中构造,它就会消失。

确保将线条剥离是绝对安全的,因为没有检查ProGuard的部分。删除任何返回的void方法都没问题,但是如果你要删除任何返回值,请确保你没有将它们用于实际的操作逻辑。

答案 5 :(得分:1)

要添加Zulaxia关于剥离Junit的答案 - Proguard已经是Android SDK / Eclipse的一部分,下一页将告诉您如何启用它。

http://developer.android.com/guide/developing/tools/proguard.html

此外,上述内容不适用于最新的默认proguard配置,因为它使用-dontoptimize标志,必须取出该标志并启用一些优化。

答案 6 :(得分:1)

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

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

assert x == 0: "Failure message";

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

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

对于生产构建,使用空语句:

;

请注意,这将控制构建时的断言,而不是运行时(通常的做法)。

我找不到现成的预处理器,所以我scripted one。请参阅处理断言的部分。要复制的许可证是here

答案 7 :(得分:0)

使用标准Java 断言关键字,例如:

assert a==b;

要实现此功能,您必须在/system/build.prop中添加一行,然后重新启动手机:

debug.assert=1

这适用于root电话。使用一些能够编辑build.prop的文件管理器(例如X-plore)。

加号:大多数(所有?)Android手机都附带断言禁用。即使您的代码意外断言为false,app也不会中断或崩溃。 但是,在您的开发设备上,您将获得断言异常。

答案 8 :(得分:-3)

API提供JUnit Assert

你可以做到

import static junit.framework.Assert.*;

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

小心不要通过eclipse导入Junit4框架,即org.junit包。你必须使用junit.framework包才能在android设备或模拟器上运行它。