tutorial room example android logging android-room

example - Android Room Database DAO registro de depuración



android room tutorial (5)

Dada una base de datos de sala DAO como esta:

import android.arch.persistence.room.Dao; import android.arch.persistence.room.Query; import java.util.Date; import java.util.List; @Dao public interface MyDao { @Query("SELECT * FROM MyTable") List<MyItem> all(); @Query("SELECT * FROM MyTable WHERE date = :date AND language = :language") MyItem byDate(Date date, String language); }

¿Hay alguna manera de agregar un Logger o algo así a MyDao para que pueda ver qué declaraciones se están realizando? Esto sería realmente útil durante el desarrollo, porque podría comprobar inmediatamente si las funciones se transforman correctamente a la instrucción SQL esperada o no.


Cuando obtengo un error desconocido al insertar o actualizar la fila en la sala db, Android no muestra ningún error en la consola de depuración. Una cosa que encontré para comprobar qué sucede mientras se realiza la depuración es:

try { someSource.update(someRow) } catch (e: Throwable) { println(e.message) }

La salida es:

Error de restricción ÚNICA: quiz.theme (código 2067)


He podido lograrlo a través de un truco para consultas Select. Esto no funcionará para las operaciones de insertar / actualizar / eliminar :)

Cree una clase separada RoomLoggingHelper como sigue

import android.annotation.SuppressLint import androidx.room.RoomSQLiteQuery private const val NULL = 1 private const val LONG = 2 private const val DOUBLE = 3 private const val STRING = 4 private const val BLOB = 5 private const val NULL_QUERY = "NULL" const val ROOM_LOGGING_TAG = "roomQueryLog" object RoomLoggingHelper { @SuppressLint("RestrictedApi") fun getStringSql(query: RoomSQLiteQuery): String { val argList = arrayListOf<String>() val bindingTypes = query.getBindingTypes() var i = 0 while (i < bindingTypes.size) { val bindingType = bindingTypes[i] when (bindingType) { NULL -> argList.add(NULL_QUERY) LONG -> argList.add(query.getLongBindings()[i].toString()) DOUBLE -> argList.add(query.getDoubleBindings()[i].toString()) STRING -> argList.add(query.getStringBindings()[i].toString()) } i++ } return String.format(query.sql.replace("?", "%s"), *argList.toArray()) } fun getStringSql(query: String?, args: Array<out Any>?): String? { return if (query != null && args != null) { String.format(query.replace("?", "%s"), *args) } else "" } } private fun RoomSQLiteQuery.getBindingTypes(): IntArray { return javaClass.getDeclaredField("mBindingTypes").let { field -> field.isAccessible = true return@let field.get(this) as IntArray } } private fun RoomSQLiteQuery.getLongBindings(): LongArray { return javaClass.getDeclaredField("mLongBindings").let { field -> field.isAccessible = true return@let field.get(this) as LongArray } } private fun RoomSQLiteQuery.getStringBindings(): Array<String> { return javaClass.getDeclaredField("mStringBindings").let { field -> field.isAccessible = true return@let field.get(this) as Array<String> } } private fun RoomSQLiteQuery.getDoubleBindings(): DoubleArray { return javaClass.getDeclaredField("mDoubleBindings").let { field -> field.isAccessible = true return@let field.get(this) as DoubleArray } } private fun RoomSQLiteQuery.getIntBindings(): IntArray { return javaClass.getDeclaredField("mBindingTypes").let { field -> field.isAccessible = true return@let field.get(this) as IntArray } }

O bien, puede descargar este archivo desde here

Agregue este archivo a su proyecto y llámelo desde su clase de base de datos de sala de la siguiente manera: Anule ambos métodos de query como este

override fun query(query: SupportSQLiteQuery?): Cursor { //This will give you the SQL String val queryString = RoomLoggingHelper.getStringSql(query as RoomSQLiteQuery) //You can log it in a way you like, I am using Timber Timber.d("$ROOM_LOGGING_TAG $queryString") return super.query(query) } override fun query(query: String?, args: Array<out Any>?): Cursor { //This will give you the SQL String val queryString = RoomLoggingHelper.getStringSql(query, args) //You can log it in a way you like, I am using Timber Timber.d("$ROOM_LOGGING_TAG $queryString") return super.query(query, args) }

Descargos de responsabilidad:

  • Estoy usando Reflection para obtener la cadena SQL, por lo tanto, use esto SOLAMENTE en el modo DEBUG
  • Esto se escribe a toda prisa y puede contener errores, sería prudente mantenerlo en el bloque try-catch
  • Además, lo he probado para la cadena args, debería funcionar por mucho tiempo y también, no funcionará para Blobs

No parece haber ningún gancho para eso en el nivel DAO. Hay devoluciones de llamadas relacionadas con la apertura de bases de datos y actualizaciones, pero no cosas arbitrarias.

Sin embargo , podría presentar una solicitud de función . Estoy de acuerdo en que podría ser útil. Aún mejor sería un marco de interceptor genérico de estilo OkHttp.


Según el document de la sala, realiza una verificación de tiempo de compilación, por lo que si su declaración SQL no es válida, la compilación en sí misma falló y se muestra un mensaje de error adecuado en el registro.

El código generado también se puede depurar de forma predeterminada y se puede encontrar en la ruta mencionada a continuación.

construir> generado> fuente> apt> su paquete> yourDao_Impl.java

Esta clase contiene la implementación de su DAO. Puede depurar esta clase al depurar otras clases en su proyecto. :-)

Ejemplo:


Suponiendo que Room utiliza Sqlite de framework como base de datos subyacente, las declaraciones se pueden registrar de manera bastante simple. La única limitación: esto se puede hacer solo en el emulador .

Desde SQLiteDebug.java :

/** * Controls the printing of SQL statements as they are executed. * * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE". */ public static final boolean DEBUG_SQL_STATEMENTS = Log.isLoggable("SQLiteStatements", Log.VERBOSE);

De forma predeterminada, el valor de log.tag.SQLiteStatements no está establecido:

alex @ mbpro: ~ $ adb shell getprop log.tag.SQLiteStatements
<- LINEA EN BLANCO ->

De acuerdo con la documentación anterior, para establecer la propiedad tenemos que utilizar:

alex @ mbpro: ~ $ adb shell setprop log.tag.SQLiteStatements VERBOSE
alex @ mbpro: ~ $ adb shell getprop log.tag.SQLiteStatements
VERBOSO

Como podemos ver, el valor VERBOSE se estableció correctamente. Sin embargo, si volvemos a ejecutar nuestra aplicación, no veremos esas declaraciones impresas. Para que funcione, tendremos que reiniciar todos los servicios usando adb shell stop y luego adb shell start .
Si intenta hacer eso con un dispositivo normal, recibirá el siguiente error (probado con Pixel XL / stock Android 9):

alex @ mbpro: ~ $ adb shell start
inicio: debe ser root
alex @ mbpro: ~ $ adb root
adbd no puede ejecutarse como root en compilaciones de producción

Por eso tenemos que usar el emulador:

alex @ mbpro: ~ $ adb root
reiniciando adbd como root
alex @ mbpro: ~ $ adb shell stop
alex @ mbpro: ~ $ adb shell start

El emulador se reiniciará.
Ejecute su aplicación y verá sentencias similares de Sqlite en logcat:

<redacted..> V/SQLiteStatements: <redacted>/my_db: "BEGIN EXCLUSIVE;" V/SQLiteStatements: <redacted>/my_db: "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)" V/SQLiteStatements: <redacted>/my_db: "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, "3cb5664b6da264c13388292d98141843")" V/SQLiteStatements: <redacted>/my_db: "CREATE TABLE IF NOT EXISTS `MyTable` (`id` TEXT NOT NULL, `date` INTEGER, `language` TEXT, PRIMARY KEY(`id`))" <redacted..> V/SQLiteStatements: <redacted>/my_db: "BEGIN EXCLUSIVE;" V/SQLiteStatements: <redacted>/my_db: "PRAGMA temp_store = MEMORY;" V/SQLiteStatements: <redacted>/my_db: "PRAGMA recursive_triggers=''ON'';" V/SQLiteStatements: <redacted>/my_db: "CREATE TEMP TABLE room_table_modification_log(version INTEGER PRIMARY KEY AUTOINCREMENT, table_id INTEGER)" V/SQLiteStatements: <redacted>/my_db: "COMMIT;" <redacted..> V/SQLiteStatements: <redacted>/my_db: "SELECT * FROM MyTable" V/SQLiteStatements: <redacted>/my_db: "SELECT * FROM MyTable WHERE date = 1551562171387 AND language = ''en''"

Para deshacer los cambios, use estos comandos:

alex @ mbpro: ~ $ adb shell setprop log.tag.SQLiteStatements / "/"
alex @ mbpro: ~ $ adb shell getprop log.tag.SQLiteStatements
<- LINEA EN BLANCO ->
alex @ mbpro: ~ $ adb shell stop
alex @ mbpro: ~ $ adb shell start
alex @ mbpro: ~ $ adb unroot
reiniciando adbd como no root