[android] Android 장치에서 assert를 사용할 수 있습니까?

내 Android 앱에서 Assert 키워드 를 사용하여 경우에 따라 에뮬레이터 또는 테스트 중에 내 장치에서 내 앱을 파괴하고 싶습니다. 이것이 가능한가?

에뮬레이터가 내 주장을 무시하는 것 같습니다.



답변

API는 JUnit Assert를 제공합니다 .

넌 할 수있어

import static junit.framework.Assert.*;

이제 junit 프레임 워크에서 제공되는 assertTrue, assertEquals, assertNull과 같은 모든 함수를 사용할 수 있습니다.

org.junit 패키지 인 eclipse를 통해 Junit4 프레임 워크를 가져 오지 않도록주의하십시오. 안드로이드 장치 또는 에뮬레이터에서 작동하려면 junit.framework 패키지를 사용해야합니다.


답변

Embedded VM Control 문서 ( 소스 트리의 원시 HTML 또는 멋진 형식의 사본)를 참조하십시오.

기본적으로 Dalvik VM은 .dex 바이트 코드에 검사를 수행하는 코드가 포함되어 있더라도 기본적으로 어설 션 검사를 무시하도록 설정됩니다. 어설 션 확인은 다음 두 가지 방법 중 하나로 설정됩니다.

(1) 다음을 통해 시스템 속성 “debug.assert”를 설정합니다.

adb shell setprop debug.assert 1

이 작업을 수행 한 후 앱을 다시 설치하는 한 의도 한대로 작동하는지 확인했습니다.

(2) 명령 줄 인수 “–enable-assert”를 dalvik VM에 전송하여 앱 개발자가 할 수있는 일이 아닐 수 있습니다 (제가 틀렸다면 누군가 저를 고쳐줍니다).

기본적으로 전역 적으로, 패키지 수준에서 또는 해당 수준에서 어설 션을 활성화하는 클래스 수준에서 설정할 수있는 플래그가 있습니다. 플래그는 기본적으로 꺼져 있으므로 어설 션 검사를 건너 뜁니다.

샘플 활동에 다음 코드를 작성했습니다.


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 키워드의 유연성은 개발 시간에 assertion을 켜고 비트 전달을 위해 끄고 대신 정상적으로 실패하는 기능입니다.

도움이 되었기를 바랍니다.


답변

어설 션이 활성화되면 assertAssertionError 되면 부울 표현식이 키워드는 단순히를 throw합니다 false.

그래서 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 문서 AssertionError 는 허용 가능한 대안으로 던지기를 권장 합니다.

프로덕션 코드에 대한 이러한 호출을 제거하도록 Proguard를 구성 할 수 있다고 생각합니다.


답변

“실제 Android”에서는 다음을 사용하는 것이 좋습니다.

$adb shell setprop dalvik.vm.enableassertions all

이 설정이 전화기에 유지되지 않으면 다음과 같은 속성으로 /data/local.prop 파일을 만들 수 있습니다.

dalvik.vm.enableassertions=all


답변

내가 Google에서 문제를 확인할 때까지 내 주장이 작동하지 않았다는 것은 지옥을 괴롭 히고 있었다. 나는 단순한 주장을 포기하고 junits 주장 방법으로 갈 것이다.

편의를 위해 다음을 사용합니다.

import static junit.framework.Assert. *;

정적 가져 오기로 인해 나중에 다음과 같이 작성할 수 있습니다.

assertTrue (…); 대신 Assert.assertTrue (…);


답변

JUnit 어설 션 (또는 다른 클래스 경로)을 사용하여 코드를 전달하는 것이 염려되는 경우 ProGuard 구성 옵션 ‘assumenosideeffects’를 사용하여 제거하면 코드에 아무런 영향이 없다는 가정하에 클래스 경로를 제거 할 수 있습니다. .

예 :

-assumenosideeffects junit.framework.Assert {
*;
}

모든 테스트 방법을 넣은 공통 디버그 라이브러리가 있으며이 옵션을 사용하여 출시 된 앱에서 제거합니다.

이것은 또한 릴리스 코드에서 사용되지 않는 조작되는 문자열의 발견하기 어려운 문제를 제거합니다. 예를 들어 디버그 로그 메서드를 작성하고 해당 메서드에서 문자열을 로깅하기 전에 디버그 모드를 확인하는 경우 여전히 문자열을 구성하고 메모리를 할당하고 메서드를 호출 한 다음 아무 작업도 수행하지 않습니다. 그런 다음 클래스를 제거하면 호출이 완전히 제거됩니다. 즉, 메서드 호출 내부에서 문자열이 생성되는 한 해당 문자열도 사라집니다.

그러나 ProGuard의 부분을 확인하지 않고 수행되므로 선을 제거하는 것이 진정으로 안전한지 확인하십시오. 무효 반환 방법을 제거하는 것은 괜찮습니다. 그러나 제거하는 항목에서 반환 값을 가져 오는 경우 실제 작동 논리에 사용하지 않는지 확인하십시오.


답변

어설 션을 사용할 수 있지만 안정적으로 사용하려면 약간의 작업이 필요합니다. 시스템 속성debug.assert 이 신뢰할 수 없습니다. 문제 175697 , 65183 , 3678617324 참조 .

한 가지 방법은 각 assert명령문을 모든 런타임이 처리 할 수있는 것으로 변환 하는 것입니다. Java 컴파일러 앞에있는 소스 전처리기로이를 수행하십시오. 예를 들어 다음 문장을 사용하십시오.

assert x == 0: "Failure message";

디버그 빌드의 경우 전처리 기는 위의 if내용을 문으로 변환합니다 .

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

프로덕션 빌드의 경우 빈 문으로 :

;

이것은 런타임 (일반적인 관행)과는 반대로 빌드 타임에 어설 션을 제어합니다.

기성품 전처리기를 찾을 수 없었기 때문에 스크립트를 작성했습니다 . 단언을 다루는 부분을 참조하십시오. 복사 할 수있는 라이센스는 여기에 있습니다 .