Можно ли использовать assert на устройствах Android? - PullRequest
89 голосов
/ 02 марта 2010

Я хочу использовать ключевое слово Assert в моих приложениях для Android, чтобы в некоторых случаях уничтожить мое приложение на эмуляторе или на моем устройстве во время тестирования. Это возможно?

Кажется, что эмулятор просто игнорирует мои утверждения.

Ответы [ 9 ]

145 голосов
/ 06 апреля 2011

См. Документ Embedded VM Control (необработанный HTML-код из исходного дерева или красиво отформатированной копии).

По сути, виртуальная машина Dalvik настроена на игнорирование проверок утверждений по умолчанию, хотя байт-код .dex содержит код для выполнения проверки. Проверка утверждений включена одним из двух способов:

(1), установив системное свойство "debug.assert" через:

adb shell setprop debug.assert 1

, который я проверил, работает так, как задумано, если вы переустановите свое приложение после этого, или

(2) отправив аргумент командной строки «--enable-assert» на виртуальную машину dalvik, что, возможно, не то, что разработчики приложений могут сделать (кто-то поправит меня, если я здесь не прав).

По сути, существует флаг, который может быть установлен либо глобально, на уровне пакета или на уровне класса, который разрешает утверждения на этом соответствующем уровне. По умолчанию этот флаг отключен, в результате чего проверки утверждений пропускаются.

Я написал следующий код в моем примере Activity:

<code>
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):

<code>
// 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</p>

<p>:
:</p>

<p>// 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</p>

<p>

Обратите внимание, как статический конструктор вызывает метод selectedAssertionStatus для объекта Class и устанавливает общеклассовую переменную $ assertionsDisabled; также обратите внимание, что в onCreate () весь код для выброса java.lang.AssertionError компилируется, но его выполнение зависит от значения $ assertionsDisabled, которое установлено для объекта Class в статическом конструкторе.

Похоже, что класс JUnit Assert используется преимущественно, поэтому, вероятно, это безопасная ставка. Гибкость ключевого слова assert заключается в возможности включения утверждений во время разработки и отключения их для доставки битов, а вместо этого изящного сбоя.

Надеюсь, это поможет.

10 голосов
/ 27 августа 2012

Когда утверждения включены, ключевое слово 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);
    }
}

Оракул java docs рекомендует , выбрасывая AssertionError в качестве приемлемой альтернативы.

Полагаю, вы можете настроить Proguard для удаления этих вызовов для производственного кода.

8 голосов
/ 13 сентября 2012

В «Android на практике» предлагается использовать:

$adb shell setprop dalvik.vm.enableassertions all

если эти настройки не сохранены на вашем телефоне, вы можете создать файл /data/local.prop со свойствами, такими как:

dalvik.vm.enableassertions=all
5 голосов
/ 27 апреля 2011

Это было чертовски неприятно из-за того, что мои утверждения не работали, пока я не проверил проблему в Google ... Я отказался от простых утверждений и буду использовать методы утверждений junits.

Для удобства я использую:

импорт статических junit.framework.Assert. *;

Из-за статического импорта я могу позже написать:

assertTrue (...); вместо Assert.assertTrue (...);

4 голосов
/ 15 сентября 2011

Если вас беспокоит доставка кода с использованием JUnit (или любого другого пути к классу), вы можете использовать конфигурационную опцию ProGuard 'accemenosideeffects', которая удалит путь к классу при условии, что его удаление ничего не даст к коду.

Например.

-assumenosideeffects junit.framework.Assert {
*;
}

У меня есть общая библиотека отладки, в которую я поместил все свои методы тестирования, а затем использую эту опцию, чтобы убрать ее из моих выпущенных приложений.

Это также устраняет трудно обнаруживаемую проблему манипулирования строками, которые никогда не используются в коде выпуска. Например, если вы пишете метод журнала отладки, и в этом методе вы проверяете режим отладки перед регистрацией строки, вы все равно создаете строку, выделяете память, вызываете метод, но затем решаете ничего не делать. Удаление класса затем удаляет вызовы полностью, то есть, пока ваша строка создается внутри вызова метода, она также исчезает.

Удостоверьтесь, что просто обрезать линии действительно безопасно, поскольку это делается без проверки со стороны ProGuard. Удаление любого метода возврата void будет хорошо, однако, если вы берете какие-либо возвращаемые значения из того, что вы удаляете, убедитесь, что вы не используете их для реальной операционной логики.

1 голос
/ 01 декабря 2015

Вы можете использовать утверждения, но для их надежного использования требуется определенная работа. Системное свойство debug.assert ненадежно; см. номера 175697 , 65183 , 36786 и 17324 .

Один из способов - перевести каждый оператор assert в то, с чем может справиться любая среда выполнения. Сделайте это с исходным препроцессором перед компилятором Java. Например, возьмем это утверждение:

assert x == 0: "Failure message";

Для отладочной сборки ваш препроцессор переведет вышеупомянутое выражение if:

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

Для производственной сборки: пустой оператор:

;

Обратите внимание, что это будет контролировать утверждения во время сборки, в отличие от времени выполнения (обычная практика).

Я не смог найти готовый препроцессор, поэтому я написал один . Смотрите часть, посвященную утверждениям. Лицензия на копирование здесь .

1 голос
/ 19 марта 2012

Чтобы добавить к ответу Zulaxia об удалении Junit - Proguard уже является частью Android SDK / Eclipse, и на следующей странице рассказывается, как его включить.

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

Кроме того, вышеописанное не будет работать с последней конфигурацией proguard по умолчанию, так как она использует флаг -dontoptimize, который должен быть удален, и некоторые оптимизации включены.

0 голосов
/ 06 августа 2012

Используйте стандартное ключевое слово Java assert , например:

assert a==b;

Чтобы это работало, вам нужно добавить одну строку в /system/build.prop и перезагрузить телефон:

debug.assert=1

Это будет работать на рутированном телефоне. Используйте какой-нибудь файловый менеджер, способный редактировать build.prop (например, X-plore).

Плюсы: большинство (все?) Телефоны Android поставляются с отключенными утверждениями. Даже если ваш код случайно установит значение false, приложение не прервется или не выйдет из строя. Однако на вашем устройстве разработки вы получите исключение утверждения.

0 голосов
/ 10 марта 2010

API предоставляет JUnit Assert .

Вы можете сделать

import static junit.framework.Assert.*;

теперь вы можете использовать все функции, такие как assertTrue, assertEquals, assertNull, которые предоставляются в инфраструктуре junit.

Будьте осторожны, чтобы не импортировать инфраструктуру Junit4 через eclipse, который будет пакетом org.junit. Вы должны использовать пакет junit.framework, чтобы он работал на устройстве Android или на эмуляторе.

...