¿Puedo usar aserrar en dispositivos Android?

88

Quiero usar la palabra clave Assert en mis aplicaciones de Android para destruir mi aplicación en algunos casos en el emulador o en mi dispositivo durante la prueba. es posible?

Parece que el emulador simplemente ignora mis afirmaciones.

Janusz
fuente
2
Para ART, en lugar de Dalvik, consulte stackoverflow.com/questions/35997703/…
fadden
1
Tenga en cuenta que la respuesta aceptada es bastante engañosa.
reducción de actividad

Respuestas:

-7

La API proporciona JUnit Assert .

Tu puedes hacer

import static junit.framework.Assert.*;

ahora puede utilizar todas las funciones como assertTrue, assertEquals, assertNull que se proporcionan en el marco junit.

Tenga cuidado de no importar el marco Junit4 a través de eclipse, ese sería el paquete org.junit. Tienes que usar el paquete junit.framework para que funcione en un dispositivo Android o en el emulador.

JRL
fuente
51
Bueno, el OP pidió una "palabra clave de aserción" que, a diferencia de junit.framework.Assert, puede ser optimizada por JIT. Y por eso mismo vine aquí. Espero que algunas de las otras respuestas sean más útiles.
Martin
27
Odio ser grosero, pero esta no debería ser la respuesta aceptada porque no responde la pregunta (estoy de acuerdo con el comentario de @Martin). Otras respuestas explican cómo hacer que la palabra clave assert funcione correctamente, por ejemplo, ejecute "adb shell setprop debug.assert 1"
jfritz42
3
Votado en contra porque OP preguntó acerca de la palabra clave assert. @scorpiodawg ha descrito el proceso a continuación: stackoverflow.com/a/5563637/484261
La respuesta de scorpiodawg llegó solo un año después, así que supongo que esta respuesta fue aceptada solo porque el OP, por alguna razón, se sintió obligado a marcar una respuesta como aceptada. Esta actitud hace que una gran cantidad de respuestas sobre SO sean incompletas o absolutamente horribles.
async
145

Consulte el documento Embedded VM Control (HTML sin formato del árbol de origen o una copia con un bonito formato ).

Básicamente, la VM Dalvik está configurada para ignorar las comprobaciones de aserción de forma predeterminada, aunque el código de bytes .dex incluye el código para realizar la comprobación. La verificación de aserciones se activa de dos formas:

(1) estableciendo la propiedad del sistema "debug.assert" a través de:

adb shell setprop debug.assert 1

que verifiqué funciona según lo previsto siempre que reinstale la aplicación después de hacer esto, o

(2) enviando el argumento de la línea de comandos "--enable-assert" a la VM dalvik, lo que podría no ser algo que los desarrolladores de aplicaciones puedan hacer (alguien me corrija si me equivoco aquí).

Básicamente, hay una bandera que se puede establecer de forma global, a nivel de paquete o a nivel de clase que habilita las afirmaciones en ese nivel respectivo. La bandera está desactivada de forma predeterminada, como resultado de lo cual se omiten las comprobaciones de aserción.

Escribí el siguiente código en mi actividad de muestra:


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;
  }
}

Para este código, el código de bytes dalvik que se genera es (para 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

Observe cómo el constructor estático invoca el método desireAssertionStatus en el objeto Class y establece la variable de clase $ assertionsDisabled; También observe que en onCreate (), todo el código para lanzar java.lang.AssertionError está compilado, pero su ejecución depende del valor de $ assertionsDisabled que se establece para el objeto Class en el constructor estático.

Parece que la clase Assert de JUnit es lo que se usa predominantemente, por lo que es probable que sea una apuesta segura usarla. La flexibilidad de la palabra clave assert es la capacidad de activar las aserciones en el momento del desarrollo y desactivarlas para enviar bits y, en su lugar, fallar correctamente.

Espero que esto ayude.

scorpiodawg
fuente
Parece que Google ha eliminado la fuente navegable (vi referencias a esto en las respuestas a otras preguntas aquí). Su mejor opción es obtener la fuente o intentar buscarla en un motor de búsqueda. Busque "dalvik / embedded-vm-control.html". Aquí hay un lugar que lo tiene: assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/… . Espero que esto ayude.
scorpiodawg
Muy útil, gracias. Sin embargo, estoy confundido por la distinción hecha en el penúltimo párrafo. Estamos discutiendo dos tipos de afirmaciones: la primera son los métodos del paquete JUnit Assert como assertNotNull (), y la segunda es la palabra clave 'assert' del lenguaje Java. ¿Su respuesta se aplica a ambos? Por ejemplo, si yo import static junit.framework.Assert.*y luego utilizo uno de sus métodos, como assertNotNull("It's null!", someObject);, ¿esta afirmación está desactivada en los bits de envío?
Jeffro
1
Hola Jeffro, no lo creo: junit.framework.Assert es simplemente una clase que lanza una excepción cuando se descubre que la condición de entrada es falsa. Por otro lado, la palabra clave assert está integrada en el lenguaje. Espero que esto ayude.
scorpiodawg
3
¿Hay alguna forma de hacerlo adb shell setprop debug.assert 1en Eclipse?
Pacerier
1
Las afirmaciones también se pueden hacer desde una terminal que se ejecuta en el dispositivo si es root. Primero su, luego setprop debug.assert 1. Tenga en cuenta que el código que muestra desensamblado permanecerá en una versión de lanzamiento ( stackoverflow.com/a/5590378/506073 ). No creo que se le pueda decir al compilador javac que no emita aserciones, por lo que deben eliminarse de alguna manera. Una solución simple para eso es envolver la palabra clave assert en su propia función que proguard puede eliminar por usted.
ahcox
10

Cuando las aserciones están habilitadas, la assertpalabra clave simplemente arroja un AssertionErrorcuando la expresión booleana es false.

Entonces, en mi opinión, la mejor alternativa, esp. si es reacio a depender de junit, es lanzar un AssertionErrorexplícitamente como se muestra a continuación:

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

Una alternativa a la declaración anterior es:

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

Donde el método se define como:

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

Los documentos de Oracle java recomiendan lanzar un AssertionErrorcomo una alternativa aceptable.

Supongo que puede configurar Proguard para eliminar estas llamadas al código de producción.

Dheeraj Vepakomma
fuente
Pero, ¿cómo habilita las afirmaciones? ¿En Android Studio?
SMBiggs
8

En "Android en la práctica" se sugiere utilizar:

$adb shell setprop dalvik.vm.enableassertions all

Si esta configuración no persiste en su teléfono, puede crear un archivo /data/local.prop con propiedades como:

dalvik.vm.enableassertions=all
Marcinj
fuente
Según stackoverflow.com/a/18556839/2004714 , es posible que también deba asegurarse de que el archivo sea de solo lectura ( chmod 644).
Paulo
5

Me estaba fastidiando muchísimo, que mis afirmaciones no funcionaran, hasta que revisé el problema en Google ... Me di por vencido con las afirmaciones simples y usaré los métodos de aserción junits.

Por razones de conveniencia, estoy usando:

importar junit.framework.Assert estático. *;

Debido a la importación estática, luego puedo escribir:

asertTrue (...); en lugar de Assert.assertTrue (...);

Ready4Android
fuente
4

Si está preocupado por el envío del código con el JUnit afirma en (o cualquier otra ruta de clase), puede usar la opción de configuración de ProGuard 'asumenosideeffects', que eliminará una ruta de clase en el supuesto de que eliminarla no afecta al código .

P.ej.

-assumenosideeffects junit.framework.Assert {
*;
}

Tengo una biblioteca de depuración común en la que coloco todos mis métodos de prueba y luego uso esta opción para eliminarla de mis aplicaciones publicadas.

Esto también elimina el problema difícil de detectar de la manipulación de cadenas que nunca se utilizan en el código de lanzamiento. Por ejemplo, si escribe un método de registro de depuración, y en ese método verifica el modo de depuración antes de registrar la cadena, todavía está construyendo la cadena, asignando memoria, llamando al método, pero luego optando por no hacer nada. Al eliminar la clase, se eliminan las llamadas por completo, es decir, siempre que su cadena se construya dentro de la llamada al método, también desaparece.

Sin embargo, asegúrese de que sea realmente seguro eliminar las líneas, ya que se hace sin verificación por parte de ProGuard. Eliminar cualquier método de devolución nulo estará bien, sin embargo, si está tomando valores de devolución de lo que esté eliminando, asegúrese de no usarlos para la lógica operativa real.

Zulaxia
fuente
1
Creo que la sintaxis adecuada sería:-assumenosideeffects class junit.framework.Assert { *; }
Pooks
Respuesta confusa. El comando mencionado provoca un error de proguard. El comando corregido por Pooks aún no elimina las afirmaciones del archivo dex binario.
Puntero nulo
Tengo el mismo problema @PointerNull. afirmar no se eliminará.
Mahdi
3

Puede usar aserciones, pero se necesita algo de trabajo para usarlas de manera confiable. La propiedad del sistema debug.assertno es confiable; consulte los números 175697 , 65183 , 36786 y 17324 .

Un método es traducir cada assertdeclaración a algo con lo que cualquier tiempo de ejecución pueda lidiar. Haga esto con un preprocesador de código fuente delante del compilador de Java. Por ejemplo, tome esta declaración:

assert x == 0: "Failure message";

Para una compilación de depuración, su preprocesador traduciría lo anterior en una ifdeclaración:

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

Para una construcción de producción, a una declaración vacía:

;

Tenga en cuenta que esto controlaría las afirmaciones en el momento de la compilación, a diferencia del tiempo de ejecución (la práctica habitual).

No pude encontrar un preprocesador listo para usar, así que escribí uno . Vea la parte que trata sobre las afirmaciones. La licencia para copiar está aquí .

Michael Allan
fuente
1

Para agregar a la respuesta de Zulaxia sobre la eliminación de Junit: Proguard ya es parte de Android SDK / Eclipse y la siguiente página le indica cómo habilitarlo.

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

Además, lo anterior no funcionará con la última configuración predeterminada de proguard porque usa el indicador -dontoptimize que debe eliminarse y algunas de las optimizaciones activadas.

Sean Moore
fuente
0

Utilice la palabra clave estándar de Java assert , por ejemplo:

assert a==b;

Para que esto funcione, debe agregar una línea a /system/build.prop y reiniciar el teléfono:

debug.assert=1

Esto funcionaría en un teléfono rooteado. Utilice algún administrador de archivos capaz de editar build.prop (por ejemplo, X-plore).

Ventajas: la mayoría (¿todos?) De los teléfonos Android se envían con las afirmaciones desactivadas. Incluso si su código afirma accidentalmente que es falso, la aplicación no interrumpirá ni fallará. Sin embargo, en su dispositivo de desarrollo obtendrá una excepción de aserción.

Puntero nulo
fuente