programacion - sqlite android studio ejemplo
Android P-''SQLite: No hay tal error de tabla'' después de copiar la base de datos de los activos (11)
Tengo una base de datos guardada en la carpeta de activos de mi aplicación y copio la base de datos utilizando el siguiente código cuando se abre la aplicación por primera vez.
inputStream = mContext.getAssets().open(Utils.getDatabaseName());
if(inputStream != null) {
int mFileLength = inputStream.available();
String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();
// Save the downloaded file
output = new FileOutputStream(filePath);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
total += count;
if(mFileLength != -1) {
// Publish the progress
publishProgress((int) (total * 100 / mFileLength));
}
output.write(data, 0, count);
}
return true;
}
El código anterior se ejecuta sin problemas, pero cuando intenta consultar la base de datos, obtiene un SQLite: no existe tal excepción de tabla.
Este problema solo ocurre en Android P, todas las versiones anteriores de Android funcionan correctamente.
¿Es este un problema conocido con Android P o ha cambiado algo?
Aquí está la solución perfecta para este problema:
Simplemente anule este método en su clase
SQLiteOpenHelper
:
@Override public void onOpen(SQLiteDatabase db) { super.onOpen(db); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { db.disableWriteAheadLogging(); } }
Desafortunadamente, la respuesta aceptada simplemente "pasa a funcionar" en casos muy concretos, pero no da un consejo que funcione constantemente para evitar un error de este tipo en Android 9.
Aquí está:
- Tenga una instancia única de la clase SQLiteOpenHelper en su aplicación para acceder a su base de datos.
- Si necesita volver a escribir / copiar la base de datos, cierre la base de datos (y cierre todas las conexiones a esta base de datos) usando el método SQLiteOpenHelper.close () de esta instancia Y no use más esta instancia de SQLiteOpenHelper.
Después de llamar a close (), no solo se cierran todas las conexiones a la base de datos, sino que los archivos de registro adicionales de la base de datos se descargan en el archivo principal .sqlite y se eliminan. Por lo tanto, solo tiene un archivo database.sqlite, listo para ser reescrito o copiado.
- Después de copiar / reescribir, etc., cree un nuevo singleton de SQLiteOpenHelper, cuyo método getWritableDatabase () devolverá una nueva instancia de la base de datos SQLite. Y úselo hasta la próxima vez que necesite copiar / reescribir su base de datos ...
Esta respuesta me ayudó a darme cuenta de eso: https://.com/a/35648781/297710
Tuve este problema en Android 9 en mi aplicación AndStatus https://github.com/andstatus/andstatus que tiene un conjunto bastante grande de pruebas automatizadas que reproducían consistentemente "SQLiteException: no tal tabla" en el emulador de Android 9 antes de esta confirmación: https://github.com/andstatus/andstatus/commit/1e3ca0eee8c9fbb8f6326b72dc4c393143a70538 Así que si eres realmente curioso, puedes ejecutar todas las pruebas antes y después de este compromiso para ver la diferencia.
Estaba teniendo un problema similar y resolvió esto agregando esto a mi SQLiteOpenHelper
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
db.disableWriteAheadLogging();
}
Al parecer, Android P establece la cosa PRAGMA Log diferente. Todavía no tengo idea de si tendrá efectos secundarios, ¡pero parece estar funcionando!
Este problema parece provocar un bloqueo mucho más frecuente en Android P que en versiones anteriores, pero no es un error en Android P en sí.
El problema es que su línea donde asigna el valor a su
String filePath
abre una conexión a la base de datos que permanece abierta cuando copia el archivo de los activos.
Para solucionar el problema, reemplace la línea.
String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();
con código para obtener el valor de la ruta del archivo y luego cerrar la base de datos:
MySQLiteOpenHelper helper = new MySQLiteOpenHelper();
SQLiteDatabase database = helper.getReadableDatabase();
String filePath = database.getPath();
database.close();
Y también agrega una clase de ayuda interna:
class MySQLiteOpenHelper extends SQLiteOpenHelper {
MySQLiteOpenHelper(Context context, String databaseName) {
super(context, databaseName, null, 2);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
La respuesta más sencilla es utilizar la siguiente línea para la ruta del archivo de la base de datos en Android PIE y superior:
DB_NAME="xyz.db";
DB_Path = "/data/data/" + BuildConfig.APPLICATION_ID + "/databases/"+DB_NAME;
Me encontré con un problema similar. Estaba copiando una base de datos pero no de un activo. Lo que encontré es que el problema no tenía nada que ver con el código de copia de mi archivo de base de datos. Tampoco tenía que ver con los archivos abiertos, no cerrados, vaciados o sincronizados. Mi código normalmente sobrescribe una base de datos sin abrir existente. Lo que parece ser nuevo / diferente con Android Pie y diferente de las versiones anteriores de Android, es que cuando Android Pie crea una base de datos SQLite, establece journal_mode en WAL (registro de escritura anticipada), de forma predeterminada. Nunca he usado el modo WAL y los documentos de SQLite dicen que journal_mode debería BORRAR de forma predeterminada. El problema es que si sobrescribo un archivo de base de datos existente, llamémoslo my.db, el registro de escritura anticipada, my.db-wal, todavía existe y efectivamente "reemplaza" lo que está en el archivo my.db recién copiado. Cuando abrí mi base de datos, la tabla sqlite_master generalmente solo contenía una fila para android_metadata. Faltaban todas las mesas que esperaba. Mi solución es simplemente restablecer journal_mode a DELETE después de abrir la base de datos, especialmente al crear una nueva base de datos con Android Pie.
PRAGMA journal_mode = DELETE;
Quizás WAL sea mejor y haya alguna forma de cerrar la base de datos para que el registro de escritura anticipada no se interponga en el camino, pero realmente no necesito WAL y no lo he necesitado para todas las versiones anteriores de Android.
Mis problemas con Android P se resolvieron agregando ''this.close ()'' después de this.getReadableDatabase () en el método createDataBase () como se muestra a continuación.
private void createDataBase() throws IOException {
this.getReadableDatabase();
this.close();
try {
copyDataBase();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Parece que no cierras el flujo de salida. Si bien es probable que no explique por qué no se crea realmente la base de datos (a menos que Android P agregue un búfer multi MB), es una buena práctica usar un try-with-resource, algo como:
// garantees that the data are flushed and the resources freed
try (FileOutputStream output = new FileOutputStream(filePath)) {
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
total += count;
if (mFileLength != -1) {
// Publish the progress
publishProgress((int) (total * 100 / mFileLength));
}
output.write(data, 0, count);
}
// maybe a bit overkill
output.getFD().sync();
}
Primero, gracias por publicar esta pregunta. Me sucedió lo mismo. Todo funcionaba bien, pero luego, cuando realizaba pruebas con Android P Preview, tenía errores. Aquí está el error que encontré para este código:
private void copyDatabase(File dbFile, String db_name) throws IOException{
InputStream is = null;
OutputStream os = null;
SQLiteDatabase db = context.openOrCreateDatabase(db_name, Context.MODE_PRIVATE, null);
db.close();
try {
is = context.getAssets().open(db_name);
os = new FileOutputStream(dbFile);
byte[] buffer = new byte[1024];
while (is.read(buffer) > 0) {
os.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
throw(e);
} finally {
try {
if (os != null) os.close();
if (is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
El problema que encontré fue que este código funciona bien, PERO en SDK 28+ openOrCreateDatabase ya no crea automáticamente la tabla android_metadata para usted. Por lo tanto, si realiza una consulta de "seleccionar * de la TABLA", no encontrará esa TABLA porque la consulta comienza a ocuparse de la "primera" tabla, que debería ser la tabla de metadatos. Arreglé esto agregando manualmente la tabla android_metadata y todo estaba bien. Espero que alguien más encuentre esto útil. Tomó una eternidad para averiguar porque las consultas específicas todavía funcionaban bien.
Problema similar, solo dispositivo Android P afectado. Todas las versiones anteriores sin problemas.
Desactivado la restauración automática en dispositivos Android 9.
Hicimos esto para solucionar problemas. No recomendaría para casos de producción.
La restauración automática colocaba una copia del archivo de la base de datos en el directorio de datos antes de que se llame a la función de copia de la base de datos en el asistente de la base de datos. Por lo tanto, el archivo file.exists () devolvió true.
La base de datos de la que se realizó la copia de seguridad del dispositivo de desarrollo faltaba en la tabla. Por lo tanto, "no se encontró la tabla" era, de hecho, correcto.
Solución sin deshabilitar el WAL
Android 9 introduce un modo especial de SQLiteDatabase llamado Compatibilidad WAL (registro de escritura anticipada) que permite que una base de datos use "journal_mode = WAL" mientras se mantiene el comportamiento de mantener un máximo de una conexión por base de datos.
En detalle aquí:
https://source.android.com/devices/tech/perf/compatibility-wal
El modo WAL de SQLite se explica en detalle aquí:
https://www.sqlite.org/wal.html
A partir de los documentos oficiales, el modo WAL agrega un segundo archivo de base de datos llamado databasename y "-wal". Entonces, si su base de datos se llama "data.db", se llama "data-wal.db" en el mismo directorio.
La solución ahora es guardar y restaurar AMBOS archivos (data.db y data-wal.db) en Android 9.
Después está funcionando como en versiones anteriores.