Могу ли я использовать assert на устройствах Android?

88

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

Похоже, эмулятор просто игнорирует мои утверждения.

Януш
источник
2
Для АРТ, а не Dalvik см stackoverflow.com/questions/35997703/...
Фадден
1
Обратите внимание, что принятый ответ вводит в заблуждение.
снижение активности

Ответы:

-7

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

Ты можешь сделать

import static junit.framework.Assert.*;

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

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

JRL
источник
51
OP запросил «ключевое слово assert», которое, в отличие от junit.framework.Assert, может быть оптимизировано JIT. И именно поэтому я приехал сюда. Надеюсь, что некоторые другие ответы будут более полезными.
Мартин
27
Я ненавижу быть грубым, но это не должен быть принятым ответом, потому что он не отвечает на вопрос (я согласен с комментарием @Martin). В других ответах объясняется, как правильно настроить функцию ключевого слова assert, например, запустить «adb shell setprop debug.assert 1»
jfritz42 08
3
Проголосовали против, потому что OP спросил о ключевом слове assert. @scorpiodawg описал процесс ниже: stackoverflow.com/a/5563637/484261
Ответ scorpiodawg пришел только год спустя, поэтому я предполагаю, что этот ответ был принят только потому, что OP по какой-то причине чувствовал себя обязанным отметить ответ как принятый. Такое отношение делает огромное количество ответов на SO либо неполными, либо откровенно ужасными.
async
145

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

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

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

adb shell setprop debug.assert 1

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

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

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

Я написал следующий код в своем образце 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

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

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

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

scorpiodawg
источник
Похоже, Google удалил доступный для просмотра источник (я видел ссылки на это в ответах на другие вопросы здесь). Лучше всего либо получить источник, либо попытаться найти его в поисковой системе. Найдите "dalvik / embedded-vm-control.html". Вот одно место, где он есть: Assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/… . Надеюсь это поможет.
scorpiodawg
Очень полезно, спасибо. Однако меня смущает различие, проведенное в предпоследнем абзаце. Мы обсуждаем два типа утверждений: первый - это методы пакета JUnit Assert, например assertNotNull (), а второй - ключевое слово assert в языке Java. Ваш ответ применим к обоим? Например, если я import static junit.framework.Assert.*и затем использую один из его методов, например assertNotNull("It's null!", someObject);, отключено ли это утверждение в битах доставки?
Джеффро
1
Привет, Джеффро, я так не думаю - junit.framework.Assert - это просто класс, который выдает исключение, когда условие ввода оказывается ложным. С другой стороны, ключевое слово assert встроено в язык. Надеюсь это поможет.
scorpiodawg
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.

Так что ИМО, лучшая альтернатива, особенно. если вы не хотите зависеть от 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
8

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

$adb shell setprop dalvik.vm.enableassertions all

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

dalvik.vm.enableassertions=all
Марцинь
источник
Согласно stackoverflow.com/a/18556839/2004714 , вам также может потребоваться убедиться, что файл доступен только для чтения ( chmod 644).
Пауло
5

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

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

import static junit.framework.Assert. *;

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

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

Ready4Android
источник
4

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

Например.

-assumenosideeffects junit.framework.Assert {
*;
}

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

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

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

Зулаксия
источник
1
Я думаю, что правильный синтаксис был бы таким:-assumenosideeffects class junit.framework.Assert { *; }
Pooks
Непонятный ответ. Упомянутая команда вызывает ошибку программы Proguard. Команда, исправленная Pooks, по-прежнему не удаляет утверждения из двоичного файла dex.
Pointer Null
У меня такая же проблема @PointerNull. assert не удаляется.
Махди
3

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

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

assert x == 0: "Failure message";

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

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

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

;

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

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

Майкл Аллан
источник
1

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

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

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

Шон Мур
источник
0

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

assert a==b;

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

debug.assert=1

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

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

Нулевой указатель
источник