Can I use assert on Android devices?
Asked Answered
B

9

89

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.

Bonsai answered 2/3, 2010 at 16:45 Comment(2)
For ART, rather than Dalvik, see #35998203Barny
Note that the accepted answer is quite misleading.Maurizio
S
-7

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.

Sexed answered 10/3, 2010 at 10:10 Comment(4)
Well the OP asked for “assert keyword” which — unlike junit.framework.Assert can be optimized by the JIT. And for this very reason I came here. Hope some of the other answers will be more helpful.Taxiway
I hate to be rude but this shouldn't be the accepted answer because it doesn't answer the question (I agree with @Taxiway 's comment). Other answers explain how to make the assert keyword function properly, e.g. run "adb shell setprop debug.assert 1"Hemicycle
Downvoted because OP asked about assert keyword. @Aiglet has outlined process below: https://mcmap.net/q/236228/-can-i-use-assert-on-android-devicesOverbuild
scorpiodawg's answer came only a year later, so I guess this answer was accepted only because the OP, for some reason, felt obliged to mark an answer as accepted. This attitude makes a huge number of answers on SO either incomplete or outright awful.Peru
A
145

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.

Aiglet answered 2/3, 2010 at 16:45 Comment(7)
It appears Google has removed the browsable source (I saw references to this in answers to other questions here). Your best bet is to either get the source, or attempt to look this up in a search engine. Search for "dalvik/embedded-vm-control.html". Here's one place that has it: assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/…. Hope this helps.Aiglet
Very useful, thank you. I am confused by the distinction made in the second-to-last paragraph, though. We are discussing two kinds of asserts: The first being the methods of the JUnit Assert package like assertNotNull(), and the second being the Java language 'assert' keyword. Does your answer apply to both? For example, if I import static junit.framework.Assert.* and then I use one of its methods, like assertNotNull("It's null!", someObject);, is this assertion turned off in shipping bits?Transpontine
Hi Jeffro, I don't believe so -- junit.framework.Assert is simply a class that throws an exception when the input condition is found to be false. The assert keyword on the other hand is built in to the language. Hope this helps.Aiglet
Is there a way to do adb shell setprop debug.assert 1 in Eclipse?Stodge
Assertions can also be done from a terminal running on the device if you are root. First su, then setprop debug.assert 1. Note that the code that you show disassembled will stay in a release build (https://mcmap.net/q/242671/-better-way-to-do-debug-only-assert-code). I don't believe the javac compiler can be told not to emit assertions so they need to be stripped-out somehow. A simple solution to that is to wrap the keyword assert in your own function that proguard can strip for you.Excogitate
@Stodge I can't find a way to run this adb shell command from Eclipse. But you can run the command after Eclipse has started the adb server and connected to your phone.Hemicycle
how can ignore assert's with pro-guard rules?Euridice
Q
10

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.

Quizzical answered 27/8, 2012 at 8:36 Comment(1)
But how do you enable assertions? On Android Studio?Natividadnativism
S
8

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
Subdivision answered 13/9, 2012 at 9:8 Comment(1)
Per https://mcmap.net/q/82530/-android-logging-levels , you may also need to make sure that file is made read-only (chmod 644).Adamsen
A
5

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(...);

Aleksandr answered 27/4, 2011 at 10:59 Comment(0)
O
4

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.

Outrange answered 15/9, 2011 at 6:51 Comment(3)
I think the proper syntax would be: -assumenosideeffects class junit.framework.Assert { *; }Aboutship
Confusing answer. Mentioned command causes proguard error. Command corrected by Pooks still doesn't remove assertions from binary dex file.Haerr
I have the same problem @PointerNull . assert wont removed.Euridice
S
3

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.

Spillar answered 1/12, 2015 at 18:35 Comment(0)
D
1

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.

Dearman answered 19/3, 2012 at 0:30 Comment(0)
H
0

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.

Haerr answered 6/8, 2012 at 8:49 Comment(0)
S
-7

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.

Sexed answered 10/3, 2010 at 10:10 Comment(4)
Well the OP asked for “assert keyword” which — unlike junit.framework.Assert can be optimized by the JIT. And for this very reason I came here. Hope some of the other answers will be more helpful.Taxiway
I hate to be rude but this shouldn't be the accepted answer because it doesn't answer the question (I agree with @Taxiway 's comment). Other answers explain how to make the assert keyword function properly, e.g. run "adb shell setprop debug.assert 1"Hemicycle
Downvoted because OP asked about assert keyword. @Aiglet has outlined process below: https://mcmap.net/q/236228/-can-i-use-assert-on-android-devicesOverbuild
scorpiodawg's answer came only a year later, so I guess this answer was accepted only because the OP, for some reason, felt obliged to mark an answer as accepted. This attitude makes a huge number of answers on SO either incomplete or outright awful.Peru

© 2022 - 2024 — McMap. All rights reserved.