Je souhaite charger une base de données externe sous Android. J'ai utilisé l'approche du tutoriel Lire la base de données externe avec SQLiteOpenHelper sur Android. J'ai créé un dossier appelé "assets" dans src / main / assets et j'y ai inséré la base de données 'Test_DB'. Malheureusement, lorsque je démarre l'application, j'obtiens le message d'erreur:

Cannot open database 'C:/Users/user/Projects/AndroidStudioProjects/Bapp/Bapp_Projekt/app/src/main/assets/Test_DB.db': Directory C:/Users/user/Projects/AndroidStudioProjects/Bapp/Bapp_Projekt/app/src/main/assets doesn't exist

Bien que la base de données 'TestDB.db' existe au chemin mentionné. Avez-vous une idée de la cause du problème et de la manière dont je peux lire ce fichier de base de données externe.

Ici vous pouvez voir le code de la classe 'DatabaseContext' pour lire la base de données externe:

package com.example.td.bapp;


import android.content.Context;
import android.content.ContextWrapper;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.util.Log;

import java.io.File;

public class DatabaseContext extends ContextWrapper {

    private static final String DEBUG_CONTEXT = "DatabaseContext";

    public DatabaseContext(Context base) {
        super(base);
    }

    @Override
    public File getDatabasePath(String name)  {

        String dbfile = "C:/Users/user/Projects/AndroidStudioProjects/Bapp/Bapp_Projekt/app/src/main/assets/" + name;
        if (!dbfile.endsWith(".db")) {
            dbfile += ".db" ;
        }

        File result = new File(dbfile);

        if (!result.getParentFile().exists()) {
            result.getParentFile().mkdirs();
        }

        if (Log.isLoggable(DEBUG_CONTEXT, Log.WARN)) {
            Log.w(DEBUG_CONTEXT, "getDatabasePath(" + name + ") = " + result.getAbsolutePath());
        }

        return result;
    }

    /* this version is called for android devices >= api-11. thank to @damccull for fixing this. */
    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
        return openOrCreateDatabase(name,mode, factory);
    }

    /* this version is called for android devices < api-11 */
    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
        SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);

        if (Log.isLoggable(DEBUG_CONTEXT, Log.WARN)) {
            Log.w(DEBUG_CONTEXT, "openOrCreateDatabase(" + name + ",,) = " + result.getPath());
        }
        return result;
    }
}

Et cette classe est appelée dans une autre classe 'DataBaseHelper' qui étend SQLiteOpenHelper et qui a le constructeur suivant

public DataBaseHelper(@Nullable Context context, String name) {
   // super(context, DATABASE, null, 1);
    super(new DatabaseContext(context), name, null, 1);

}

J'apprécierai chaque instant et serai très reconnaissant pour votre aide.

Je pense que le problème est peut-être que j'ai créé moi-même le dossier «assets», car il n'existait pas auparavant. Comment puis-je dire à Android que ce dossier «actifs» doit être inclus dans l'application?

MISE À JOUR : J'ai fait ce que MikeT m'a conseillé et j'utilise SQLiteAssetHelper SQLiteAssetHelper. La base de données peut maintenant être chargée. Cependant, chaque requête de la base de données ne renvoie rien comme si la base de données était vide. Ce n'est certainement pas le cas. Par exemple, la requête suivante yiels un rowCount de 0 qui n'est pas vrai

public Cursor getDataDB_TableItemNamesByItemType(String itemType) {

    SQLiteDatabase db = this.getWritableDatabase();
    Cursor res = db.rawQuery("select * from " + TABLE_ITEM + " where "
            + ITEM_TYPE + " = '" + itemType+ "'", null);
    Log.e("LogTag", "res.getCount(): " + res.getCount());
    return res;

}

Si j'effectue exactement la même requête sur la base de données SQL, j'obtiens un nombre de lignes positif (en fonction de l'argument itemType). Pouvez-vous imaginer quel est le problème et pourquoi ma base de données lue en externe semble être vide?

0
VanessaF 26 févr. 2021 à 21:04

1 réponse

Supplémentaire

En ce qui concerne les commentaires: -

Fondamentalement, la base de données est modifiée tout le temps de manière dynamique par l'utilisateur à mesure que de nouveaux éléments sont ajoutés et supprimés en fonction de l'itération de l'utilisateur. En outre, la base de données doit être modifiée de temps à autre en externe. Comme indiqué précédemment, je peux facilement archiver la mise à jour en renommant simplement la base de données et je me demandais s'il existe un moyen plus pratique pour cela (normalement, il devrait exister). Cependant, lorsque vous regardez votre code suggéré, cela semble beaucoup plus complexe que de simplement changer le nom de la base de données. Donc, je suppose qu'il n'y a pas de solution facile pour cela.

Je pense que vous devez considérer deux fonctionnalités distinctes.

C'est-à-dire 1) conserver les données de l'utilisateur de l'application et 2) modifier les données fournies par l'application.

Vous pouvez les séparer en utilisant des bases de données séparées mais connectées, une autre façon pourrait être de différencier les deux types en fonction de la dénomination des entités (une entité étant des tables déclenche des vues, etc.).

  • par exemple. les entités fournies par l'application sont appelées as_ ou les entités de l'utilisateur appelées user_ ou les deux.

Lorsqu'un nouveau fichier d'actif est détecté, vous pouvez renommer le fichier existant en old_TestDB.db, puis copier le nouveau fichier dans TestDB.db. Vous pouvez ensuite supprimer toutes les lignes des entités de l'utilisateur (peut-être pas nécessaire si vous ne fournissez jamais les données de l'utilisateur), puis connecter le old_TestDB.db, copier les données de l'ancien vers le nouveau, supprimer la connexion vers old_TestDB.db, et enfin supprimer le old_TestDB .db (ou peut-être gardez-le juste au cas où il y aurait un problème afin que vous puissiez avoir quelque chose à restaurer).

La détection du nouveau fichier d'actif à l'aide des noms de fichier peut être problématique, car vous risquez de vous retrouver avec de nombreux anciens fichiers dans le dossier d'actifs. En tant que tel, il pourrait être plus simple et plus favorable pour la taille de l'application si le même nom de fichier était utilisé MAIS la version_utilisateur sqlite a été utilisée.

Le sqlite user_version est un numéro de version stocké dans les données d'en-tête de la base de données. C'est 4 octets avec un décalage de 60 octets dans l'en-tête. Vous pouvez y accéder sans les frais généraux liés à l'ouverture de la base de données en utilisant IO standard (comme lorsque le fichier d'actif est copié). Vous compareriez cela à la base de données déjà existante et, si elle est différente, gérez le nouveau fichier d'actif.

Vous pouvez changer la version utilisateur en utilisant le SQL PRAGMA user_version = n (certains éditeurs vous permettent de changer cela via une fenêtre) https://www.sqlite.org/pragma.html#pragma_user_version

Une approche alternative consisterait à ne pas essayer de détecter un actif modifié, ni d'utiliser la version_utilisateur SQLite mais de toujours supposer un actif modifié et donc, lorsqu'une nouvelle version de l'application est introduite, d'incrémenter en plus la VERSION DE LA BASE DE DONNÉES qui est transmise à SQLiteOpenHelper et à puis effectuez la copie du fichier d'actif et appliquez les données de l'utilisateur dans la méthode onUpgrade. Cependant, cela peut être semé d'embûches / problèmes car la base de données a été ouverte (pour extraire la version utilisateur de la base de données, la version user_version sera remplacée par la VERSION BASE DE DONNÉES de l'application).

Cependant, vous devez d'abord concevoir la base de données en conséquence avant de commencer à écrire du code.

1
MikeT 7 mars 2021 à 09:47