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

发布于 2024-08-23 07:39:45 字数 225 浏览 6 评论 0原文

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

看来模拟器只是忽略了我的断言。

I want to use the Assert keyword in my android apps to destroy my app in some cases on the emulator, or my device during testing. Is this possible?

It seems that the emulator just ignores my asserts.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

赏烟花じ飞满天 2024-08-30 07:39:45

请参阅嵌入式 VM 控制文档(来自 源树,或格式良好的复制)。

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

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

adb shell setprop debug.assert 1

我验证了只要您在执行此操作后重新安装应用程序即可按预期工作,或者

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

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

我在示例 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 对象上的desiredAssertionStatus 方法并设置类范围变量$assertionsDisabled;还要注意,在 onCreate() 中,所有抛出 java.lang.AssertionError 的代码都被编译进去,但其执行取决于静态构造函数中为 Class 对象设置的 $assertionsDisabled 的值。

看来 JUnit 的 Assert 类是主要使用的类,因此使用它可能是一个安全的选择。 assert 关键字的灵活性在于能够在开发时打开断言,并在传送位时将其关闭,然后优雅地失败。

See the Embedded VM Control document (raw HTML from the source tree, or a nicely formatted copy).

Basically, the Dalvik VM is set to ignore assertion checks by default, even though the .dex byte code includes the code to perform the check. Checking assertions is turned on in one of two ways:

(1) by setting the system property "debug.assert" via:

adb shell setprop debug.assert 1

which I verified works as intended as long as you reinstall your app after doing this, or

(2) by sending the command line argument "--enable-assert" to the dalvik VM which might not be something app developers are likely to be able to do (somebody correct me if I'm wrong here).

Basically, there is a flag that can be set either globally, at a package level, or at a class level which enables assertions at that respective level. The flag is off by default, as a result of which the assertion checks are skipped.

I wrote the following code in my sample 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;
  }
}

For this code, the dalvik byte code that is generated is (for 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

Notice how the static constructor invokes the method desiredAssertionStatus on the Class object and sets the class-wide variable $assertionsDisabled; also notice that in onCreate(), all of the code to throw java.lang.AssertionError is compiled in, but its execution is contingent upon the value of $assertionsDisabled which is set for the Class object in the static constructor.

It appears that JUnit's Assert class is what is used predominantly, so it is likely a safe bet to use that. The flexibility of the assert keyword is the ability to turn on assertions at development time and turn them off for shipping bits and instead fail gracefully.

我是有多爱你 2024-08-30 07:39:45

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

所以在我看来,最好的选择,尤其是。如果您不愿意依赖 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 来删除这些对生产代码的调用。

When assertions are enabled, the assert keyword simply throws an AssertionError when the boolean expression is false.

So IMO, the best alternative, esp. if you're averse to depend on junit, is to throw an AssertionError explicitly as shown below:

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

An alternative to the above statement is:

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

Where the method is defined as:

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

The Oracle java docs recommend throwing an AssertionError as an acceptable alternative.

I guess you can configure Proguard to strip out these calls for production code.

半夏半凉 2024-08-30 07:39:45

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

$adb shell setprop dalvik.vm.enableassertions all

如果此设置未保留在您的手机上,那么您可以创建 /data/local.prop 文件,其属性如下:

dalvik.vm.enableassertions=all

In "Android in Practice" it is suggested to use:

$adb shell setprop dalvik.vm.enableassertions all

if this settings is not persisted on your phone then you can create /data/local.prop file with properties like:

dalvik.vm.enableassertions=all
一杆小烟枪 2024-08-30 07:39:45

我的断言不起作用,这让我很烦恼,直到我在谷歌上检查了这个问题……我放弃了简单的断言,转而使用 junits 断言方法。

为了方便起见,我使用:

import static junit.framework.Assert.*;

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

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

It was bugging the hell out of me, that my assertions didnt work, until I checked the issue out on google... I gave up on simple assertions and will go with junits assertion methods.

For convenience purposes I am using:

import static junit.framework.Assert.*;

Due to the static import I can later write:

assertTrue(...); instead of Assert.assertTrue(...);

伪心 2024-08-30 07:39:45

如果您担心使用 JUnit 断言(或任何其他类路径)传送代码,您可以使用 ProGuard 配置选项“assumenosideeffects”,这将删除类路径,假设删除它对代码没有任何作用。

例如。

-assumenosideeffects junit.framework.Assert {
*;
}

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

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

不过,请确保删除这些行确实是安全的,因为 ProGuard 不会对此进行检查。删除任何 void 返回方法就可以了,但是如果您从要删除的内容中获取任何返回值,请确保您没有将它们用于实际的操作逻辑。

If you're concerned about shipping code with the JUnit asserts in (or any other class path), you can use the ProGuard config option 'assumenosideeffects', which will strip out a class path on the assumption that removing it does nothing to the code.

Eg.

-assumenosideeffects junit.framework.Assert {
*;
}

I have a common debug library I put all my testing methods in, and then use this option to strip it from my released apps.

This also removes the hard to spot problem of strings being manipulated that are never used in release code. For example if you write a debug log method, and in that method you check for debug mode before logging the string, you are still constructing the string, allocating memory, calling the method, but then opting to do nothing. Stripping the class out then removes the calls entirely, meaning as long as your string is constructed inside the method call, it goes away as well.

Make sure it is genuinely safe to just strip the lines out however, as it is done with no checking on ProGuard's part. Removing any void returning method will be fine, however if you are taking any return values from whatever you are removing, make sure you aren't using them for actual operational logic.

只是在用心讲痛 2024-08-30 07:39:45

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

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

assert x == 0: "Failure message";

对于调试构建,预处理器会将上述内容转换为 if 语句:

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

对于生产构建,转换为空语句:

;

请注意,这将在构建时控制断言,与运行时(通常的做法)相反。

我找不到现成的预处理器,所以我编写了一个脚本。请参阅处理断言的部分。复制许可证位于此处

You can use assertions, but it takes some work to use them reliably. System property debug.assert is unreliable; see issues 175697, 65183, 36786 and 17324.

One method is to translate each assert statement to something any runtime can deal with. Do this with a source preprocessor in front of the Java compiler. For example, take this statement:

assert x == 0: "Failure message";

For a debug build, your preprocessor would translate the above to an if statement:

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

For a production build, to an empty statement:

;

Note that this would control assertions at build time, as opposed to run time (the usual practice).

I could find no ready-made preprocessor, so I scripted one. See the part dealing with assertions. Licence to copy is here.

想你只要分分秒秒 2024-08-30 07:39:45

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

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

另外,上面的内容不适用于最新的默认混淆器配置,因为它使用 -dontoptimize 标志,必须删除该标志并打开一些优化。

To add to Zulaxia's answer on stripping out Junit - Proguard is already part of Android SDK /Eclipse and the following page tells you how to enable it.

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

Also the above wont work with the latest default proguard configuration because it uses the -dontoptimize flag which must be taken out and some of the optimizations turned on.

夢归不見 2024-08-30 07:39:45

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

assert a==b;

为此,您必须向 /system/build.prop 添加一行,然后重新启动手机:

debug.assert=1

这将在已 root 的手机上运行。使用一些能够编辑 build.prop 的文件管理器(例如 X-plore)。

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

Use standard Java assert keyword, for example:

assert a==b;

For this to work, you have to add one line to /system/build.prop, and reboot phone:

debug.assert=1

This would work on rooted phone. Use some file manager capable to edit build.prop (e.g. X-plore).

Pluses: most (all?) Android phones ship with assertions disabled. Even if your code accidentally asserts to false, app won't interrupt or crash.
However, on your development device you'll get assertion exception.

掌心的温暖 2024-08-30 07:39:45

该 API 提供 JUnit 断言

您现在可以

import static junit.framework.Assert.*;

使用 junit 框架中提供的所有函数,如assertTrue、assertEquals、assertNull。

注意不要通过eclipse导入Junit4框架,那将是org.junit包。您必须使用 junit.framework 包才能使其在 Android 设备或模拟器上运行。

The API provides the JUnit Assert.

You can do

import static junit.framework.Assert.*;

now you can use all the functions like assertTrue, assertEquals, assertNull that are provided in the junit framework.

Be careful not to import the Junit4 framework through eclipse, that would be the org.junit package. You have to use the junit.framework package to get it working on an android device or the emulator.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文