Expédier une application avec une base de données

959

Si votre application nécessite une base de données et qu'elle contient des données intégrées, quelle est la meilleure façon d'expédier cette application? Devrais-je:

  1. Précréer la base de données SQLite et l'inclure dans le .apk?

  2. Inclure les commandes SQL avec l'application et faire créer la base de données et insérer les données lors de la première utilisation?

Les inconvénients que je vois sont:

  1. D'éventuelles discordances entre les versions de SQLite pourraient causer des problèmes et je ne sais pas actuellement où la base de données doit aller et comment y accéder.

  2. La création et le remplissage de la base de données sur l'appareil peuvent prendre beaucoup de temps.

Aucune suggestion? Des pointeurs vers la documentation concernant tout problème seraient grandement appréciés.

Heikki Toivonen
la source
6
utiliser SQLiteAssetHelper
Richard Le Mesurier

Réponses:

199

Il existe deux options pour créer et mettre à jour des bases de données.

L'une consiste à créer une base de données en externe, puis à la placer dans le dossier des ressources du projet, puis à copier la base de données entière à partir de là. C'est beaucoup plus rapide si la base de données contient beaucoup de tables et d'autres composants. Les mises à niveau sont déclenchées en modifiant le numéro de version de la base de données dans le fichier res / values ​​/ strings.xml. Les mises à niveau seraient ensuite réalisées en créant une nouvelle base de données en externe, en remplaçant l'ancienne base de données du dossier d'actifs par la nouvelle base de données, en enregistrant l'ancienne base de données dans le stockage interne sous un autre nom, en copiant la nouvelle base de données du dossier d'actifs dans le stockage interne, en transférant tous des données de l'ancienne base de données (renommée précédemment) dans la nouvelle base de données et enfin la suppression de l'ancienne base de données. Vous pouvez créer une base de données à l'origine en utilisant lePlugin SQLite Manager FireFox pour exécuter vos instructions SQL de création.

L'autre option consiste à créer une base de données en interne à partir d'un fichier sql. Ce n'est pas aussi rapide, mais le délai serait probablement imperceptible pour les utilisateurs si la base de données ne contient que quelques tables. Les mises à niveau sont déclenchées en modifiant le numéro de version de la base de données dans le fichier res / values ​​/ strings.xml. Les mises à niveau seraient alors effectuées en traitant un fichier sql de mise à niveau. Les données de la base de données resteront inchangées, sauf lorsque son conteneur est supprimé, par exemple en supprimant une table.

L'exemple ci-dessous montre comment utiliser l'une ou l'autre méthode.

Voici un exemple de fichier create_database.sql. Il doit être placé dans le dossier des ressources du projet pour la méthode interne ou copié dans le "Exécuter SQL" de SQLite Manager pour créer la base de données pour la méthode externe. (REMARQUE: notez le commentaire sur la table requise par Android.)

--Android requires a table named 'android_metadata' with a 'locale' column
CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'en_US');
INSERT INTO "android_metadata" VALUES ('en_US');

CREATE TABLE "kitchen_table";
CREATE TABLE "coffee_table";
CREATE TABLE "pool_table";
CREATE TABLE "dining_room_table";
CREATE TABLE "card_table"; 

Voici un exemple de fichier update_database.sql. Il doit être placé dans le dossier des ressources du projet pour la méthode interne ou copié dans le "Exécuter SQL" de SQLite Manager pour créer la base de données pour la méthode externe. (REMARQUE: notez que les trois types de commentaires SQL seront ignorés par l'analyseur sql inclus dans cet exemple.)

--CREATE TABLE "kitchen_table";  This is one type of comment in sql.  It is ignored by parseSql.
/*
 * CREATE TABLE "coffee_table"; This is a second type of comment in sql.  It is ignored by parseSql.
 */
{
CREATE TABLE "pool_table";  This is a third type of comment in sql.  It is ignored by parseSql.
}
/* CREATE TABLE "dining_room_table"; This is a second type of comment in sql.  It is ignored by parseSql. */
{ CREATE TABLE "card_table"; This is a third type of comment in sql.  It is ignored by parseSql. }

--DROP TABLE "picnic_table"; Uncomment this if picnic table was previously created and now is being replaced.
CREATE TABLE "picnic_table" ("plates" TEXT);
INSERT INTO "picnic_table" VALUES ('paper');

Voici une entrée à ajouter au fichier /res/values/strings.xml pour le numéro de version de la base de données.

<item type="string" name="databaseVersion" format="integer">1</item>

Voici une activité qui accède à la base de données puis l'utilise. ( Remarque: vous souhaiterez peut-être exécuter le code de la base de données dans un thread distinct s'il utilise beaucoup de ressources. )

package android.example;

import android.app.Activity;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;

/**
 * @author Danny Remington - MacroSolve
 * 
 *         Activity for demonstrating how to use a sqlite database.
 */
public class Database extends Activity {
     /** Called when the activity is first created. */
     @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        DatabaseHelper myDbHelper;
        SQLiteDatabase myDb = null;

        myDbHelper = new DatabaseHelper(this);
        /*
         * Database must be initialized before it can be used. This will ensure
         * that the database exists and is the current version.
         */
         myDbHelper.initializeDataBase();

         try {
            // A reference to the database can be obtained after initialization.
            myDb = myDbHelper.getWritableDatabase();
            /*
             * Place code to use database here.
             */
         } catch (Exception ex) {
            ex.printStackTrace();
         } finally {
            try {
                myDbHelper.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                myDb.close();
            }
        }

    }
}

Voici la classe d'assistance de base de données dans laquelle la base de données est créée ou mise à jour si nécessaire. (REMARQUE: Android nécessite que vous créiez une classe qui étend SQLiteOpenHelper afin de fonctionner avec une base de données Sqlite.)

package android.example;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * @author Danny Remington - MacroSolve
 * 
 *         Helper class for sqlite database.
 */
public class DatabaseHelper extends SQLiteOpenHelper {

    /*
     * The Android's default system path of the application database in internal
     * storage. The package of the application is part of the path of the
     * directory.
     */
    private static String DB_DIR = "/data/data/android.example/databases/";
    private static String DB_NAME = "database.sqlite";
    private static String DB_PATH = DB_DIR + DB_NAME;
    private static String OLD_DB_PATH = DB_DIR + "old_" + DB_NAME;

    private final Context myContext;

    private boolean createDatabase = false;
    private boolean upgradeDatabase = false;

    /**
     * Constructor Takes and keeps a reference of the passed context in order to
     * access to the application assets and resources.
     * 
     * @param context
     */
    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, context.getResources().getInteger(
                R.string.databaseVersion));
        myContext = context;
        // Get the path of the database that is based on the context.
        DB_PATH = myContext.getDatabasePath(DB_NAME).getAbsolutePath();
    }

    /**
     * Upgrade the database in internal storage if it exists but is not current. 
     * Create a new empty database in internal storage if it does not exist.
     */
    public void initializeDataBase() {
        /*
         * Creates or updates the database in internal storage if it is needed
         * before opening the database. In all cases opening the database copies
         * the database in internal storage to the cache.
         */
        getWritableDatabase();

        if (createDatabase) {
            /*
             * If the database is created by the copy method, then the creation
             * code needs to go here. This method consists of copying the new
             * database from assets into internal storage and then caching it.
             */
            try {
                /*
                 * Write over the empty data that was created in internal
                 * storage with the one in assets and then cache it.
                 */
                copyDataBase();
            } catch (IOException e) {
                throw new Error("Error copying database");
            }
        } else if (upgradeDatabase) {
            /*
             * If the database is upgraded by the copy and reload method, then
             * the upgrade code needs to go here. This method consists of
             * renaming the old database in internal storage, create an empty
             * new database in internal storage, copying the database from
             * assets to the new database in internal storage, caching the new
             * database from internal storage, loading the data from the old
             * database into the new database in the cache and then deleting the
             * old database from internal storage.
             */
            try {
                FileHelper.copyFile(DB_PATH, OLD_DB_PATH);
                copyDataBase();
                SQLiteDatabase old_db = SQLiteDatabase.openDatabase(OLD_DB_PATH, null, SQLiteDatabase.OPEN_READWRITE);
                SQLiteDatabase new_db = SQLiteDatabase.openDatabase(DB_PATH,null, SQLiteDatabase.OPEN_READWRITE);
                /*
                 * Add code to load data into the new database from the old
                 * database and then delete the old database from internal
                 * storage after all data has been transferred.
                 */
            } catch (IOException e) {
                throw new Error("Error copying database");
            }
        }

    }

    /**
     * Copies your database from your local assets-folder to the just created
     * empty database in the system folder, from where it can be accessed and
     * handled. This is done by transfering bytestream.
     * */
    private void copyDataBase() throws IOException {
        /*
         * Close SQLiteOpenHelper so it will commit the created empty database
         * to internal storage.
         */
        close();

        /*
         * Open the database in the assets folder as the input stream.
         */
        InputStream myInput = myContext.getAssets().open(DB_NAME);

        /*
         * Open the empty db in interal storage as the output stream.
         */
        OutputStream myOutput = new FileOutputStream(DB_PATH);

        /*
         * Copy over the empty db in internal storage with the database in the
         * assets folder.
         */
        FileHelper.copyFile(myInput, myOutput);

        /*
         * Access the copied database so SQLiteHelper will cache it and mark it
         * as created.
         */
        getWritableDatabase().close();
    }

    /*
     * This is where the creation of tables and the initial population of the
     * tables should happen, if a database is being created from scratch instead
     * of being copied from the application package assets. Copying a database
     * from the application package assets to internal storage inside this
     * method will result in a corrupted database.
     * <P>
     * NOTE: This method is normally only called when a database has not already
     * been created. When the database has been copied, then this method is
     * called the first time a reference to the database is retrieved after the
     * database is copied since the database last cached by SQLiteOpenHelper is
     * different than the database in internal storage.
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        /*
         * Signal that a new database needs to be copied. The copy process must
         * be performed after the database in the cache has been closed causing
         * it to be committed to internal storage. Otherwise the database in
         * internal storage will not have the same creation timestamp as the one
         * in the cache causing the database in internal storage to be marked as
         * corrupted.
         */
        createDatabase = true;

        /*
         * This will create by reading a sql file and executing the commands in
         * it.
         */
            // try {
            // InputStream is = myContext.getResources().getAssets().open(
            // "create_database.sql");
            //
            // String[] statements = FileHelper.parseSqlFile(is);
            //
            // for (String statement : statements) {
            // db.execSQL(statement);
            // }
            // } catch (Exception ex) {
            // ex.printStackTrace();
            // }
    }

    /**
     * Called only if version number was changed and the database has already
     * been created. Copying a database from the application package assets to
     * the internal data system inside this method will result in a corrupted
     * database in the internal data system.
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        /*
         * Signal that the database needs to be upgraded for the copy method of
         * creation. The copy process must be performed after the database has
         * been opened or the database will be corrupted.
         */
        upgradeDatabase = true;

        /*
         * Code to update the database via execution of sql statements goes
         * here.
         */

        /*
         * This will upgrade by reading a sql file and executing the commands in
         * it.
         */
        // try {
        // InputStream is = myContext.getResources().getAssets().open(
        // "upgrade_database.sql");
        //
        // String[] statements = FileHelper.parseSqlFile(is);
        //
        // for (String statement : statements) {
        // db.execSQL(statement);
        // }
        // } catch (Exception ex) {
        // ex.printStackTrace();
        // }
    }

    /**
     * Called everytime the database is opened by getReadableDatabase or
     * getWritableDatabase. This is called after onCreate or onUpgrade is
     * called.
     */
    @Override
    public void onOpen(SQLiteDatabase db) {
        super.onOpen(db);
    }

    /*
     * Add your public helper methods to access and get content from the
     * database. You could return cursors by doing
     * "return myDataBase.query(....)" so it'd be easy to you to create adapters
     * for your views.
     */

}

Voici la classe FileHelper qui contient des méthodes pour copier des fichiers de flux d'octets et analyser des fichiers SQL.

package android.example;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.channels.FileChannel;

/**
 * @author Danny Remington - MacroSolve
 * 
 *         Helper class for common tasks using files.
 * 
 */
public class FileHelper {
    /**
     * Creates the specified <i><b>toFile</b></i> that is a byte for byte a copy
     * of <i><b>fromFile</b></i>. If <i><b>toFile</b></i> already existed, then
     * it will be replaced with a copy of <i><b>fromFile</b></i>. The name and
     * path of <i><b>toFile</b></i> will be that of <i><b>toFile</b></i>. Both
     * <i><b>fromFile</b></i> and <i><b>toFile</b></i> will be closed by this
     * operation.
     * 
     * @param fromFile
     *            - InputStream for the file to copy from.
     * @param toFile
     *            - InputStream for the file to copy to.
     */
    public static void copyFile(InputStream fromFile, OutputStream toFile) throws IOException {
        // transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[1024];
        int length;

        try {
            while ((length = fromFile.read(buffer)) > 0) {
                toFile.write(buffer, 0, length);
            }
        }
        // Close the streams
        finally {
            try {
                if (toFile != null) {
                    try {
                        toFile.flush();
                    } finally {
                        toFile.close();
                    }
            }
            } finally {
                if (fromFile != null) {
                    fromFile.close();
                }
            }
        }
    }

    /**
     * Creates the specified <i><b>toFile</b></i> that is a byte for byte a copy
     * of <i><b>fromFile</b></i>. If <i><b>toFile</b></i> already existed, then
     * it will be replaced with a copy of <i><b>fromFile</b></i>. The name and
     * path of <i><b>toFile</b></i> will be that of <i><b>toFile</b></i>. Both
     * <i><b>fromFile</b></i> and <i><b>toFile</b></i> will be closed by this
     * operation.
     * 
     * @param fromFile
     *            - String specifying the path of the file to copy from.
     * @param toFile
     *            - String specifying the path of the file to copy to.
     */
    public static void copyFile(String fromFile, String toFile) throws IOException {
        copyFile(new FileInputStream(fromFile), new FileOutputStream(toFile));
    }

    /**
     * Creates the specified <i><b>toFile</b></i> that is a byte for byte a copy
     * of <i><b>fromFile</b></i>. If <i><b>toFile</b></i> already existed, then
     * it will be replaced with a copy of <i><b>fromFile</b></i>. The name and
     * path of <i><b>toFile</b></i> will be that of <i><b>toFile</b></i>. Both
     * <i><b>fromFile</b></i> and <i><b>toFile</b></i> will be closed by this
     * operation.
     * 
     * @param fromFile
     *            - File for the file to copy from.
     * @param toFile
     *            - File for the file to copy to.
     */
    public static void copyFile(File fromFile, File toFile) throws IOException {
        copyFile(new FileInputStream(fromFile), new FileOutputStream(toFile));
    }

    /**
     * Creates the specified <i><b>toFile</b></i> that is a byte for byte a copy
     * of <i><b>fromFile</b></i>. If <i><b>toFile</b></i> already existed, then
     * it will be replaced with a copy of <i><b>fromFile</b></i>. The name and
     * path of <i><b>toFile</b></i> will be that of <i><b>toFile</b></i>. Both
     * <i><b>fromFile</b></i> and <i><b>toFile</b></i> will be closed by this
     * operation.
     * 
     * @param fromFile
     *            - FileInputStream for the file to copy from.
     * @param toFile
     *            - FileInputStream for the file to copy to.
     */
    public static void copyFile(FileInputStream fromFile, FileOutputStream toFile) throws IOException {
        FileChannel fromChannel = fromFile.getChannel();
        FileChannel toChannel = toFile.getChannel();

        try {
            fromChannel.transferTo(0, fromChannel.size(), toChannel);
        } finally {
            try {
                if (fromChannel != null) {
                    fromChannel.close();
                }
            } finally {
                if (toChannel != null) {
                    toChannel.close();
                }
            }
        }
    }

    /**
     * Parses a file containing sql statements into a String array that contains
     * only the sql statements. Comments and white spaces in the file are not
     * parsed into the String array. Note the file must not contained malformed
     * comments and all sql statements must end with a semi-colon ";" in order
     * for the file to be parsed correctly. The sql statements in the String
     * array will not end with a semi-colon ";".
     * 
     * @param sqlFile
     *            - String containing the path for the file that contains sql
     *            statements.
     * 
     * @return String array containing the sql statements.
     */
    public static String[] parseSqlFile(String sqlFile) throws IOException {
        return parseSqlFile(new BufferedReader(new FileReader(sqlFile)));
    }

    /**
     * Parses a file containing sql statements into a String array that contains
     * only the sql statements. Comments and white spaces in the file are not
     * parsed into the String array. Note the file must not contained malformed
     * comments and all sql statements must end with a semi-colon ";" in order
     * for the file to be parsed correctly. The sql statements in the String
     * array will not end with a semi-colon ";".
     * 
     * @param sqlFile
     *            - InputStream for the file that contains sql statements.
     * 
     * @return String array containing the sql statements.
     */
    public static String[] parseSqlFile(InputStream sqlFile) throws IOException {
        return parseSqlFile(new BufferedReader(new InputStreamReader(sqlFile)));
    }

    /**
     * Parses a file containing sql statements into a String array that contains
     * only the sql statements. Comments and white spaces in the file are not
     * parsed into the String array. Note the file must not contained malformed
     * comments and all sql statements must end with a semi-colon ";" in order
     * for the file to be parsed correctly. The sql statements in the String
     * array will not end with a semi-colon ";".
     * 
     * @param sqlFile
     *            - Reader for the file that contains sql statements.
     * 
     * @return String array containing the sql statements.
     */
    public static String[] parseSqlFile(Reader sqlFile) throws IOException {
        return parseSqlFile(new BufferedReader(sqlFile));
    }

    /**
     * Parses a file containing sql statements into a String array that contains
     * only the sql statements. Comments and white spaces in the file are not
     * parsed into the String array. Note the file must not contained malformed
     * comments and all sql statements must end with a semi-colon ";" in order
     * for the file to be parsed correctly. The sql statements in the String
     * array will not end with a semi-colon ";".
     * 
     * @param sqlFile
     *            - BufferedReader for the file that contains sql statements.
     * 
     * @return String array containing the sql statements.
     */
    public static String[] parseSqlFile(BufferedReader sqlFile) throws IOException {
        String line;
        StringBuilder sql = new StringBuilder();
        String multiLineComment = null;

        while ((line = sqlFile.readLine()) != null) {
            line = line.trim();

            // Check for start of multi-line comment
            if (multiLineComment == null) {
                // Check for first multi-line comment type
                if (line.startsWith("/*")) {
                    if (!line.endsWith("}")) {
                        multiLineComment = "/*";
                    }
                // Check for second multi-line comment type
                } else if (line.startsWith("{")) {
                    if (!line.endsWith("}")) {
                        multiLineComment = "{";
                }
                // Append line if line is not empty or a single line comment
                } else if (!line.startsWith("--") && !line.equals("")) {
                    sql.append(line);
                } // Check for matching end comment
            } else if (multiLineComment.equals("/*")) {
                if (line.endsWith("*/")) {
                    multiLineComment = null;
                }
            // Check for matching end comment
            } else if (multiLineComment.equals("{")) {
                if (line.endsWith("}")) {
                    multiLineComment = null;
                }
            }

        }

        sqlFile.close();

        return sql.toString().split(";");
    }

}
Danny Remington - OMS
la source
j'ai utilisé le code ci-dessus pour mettre à niveau ma base de données "upgrade_database.sql contient une instruction d'insertion. J'ai remarqué que l'insertion mentionnée ci-dessus ne devient pas esecute en raison du point-virgule dans les valeurs et comment résoudre ce problème.
Sam
5
Il existe une troisième option - copier la base de données à partir du Web. Je l'ai fait et ça va assez rapidement pour un db de 4 meg. Il résout également le problème avec 2.3, pour lequel la première solution (copie db) ne fonctionne pas.
Jack BeNimble le
2
Danny And Austyn - Votre solution était parfaite. J'avais des problèmes avec ma solution maison et je suis tombé sur la vôtre. Cela a vraiment frappé l'endroit. Merci d'avoir pris le temps de nous le fournir.
George Baker
4
Je préfère de beaucoup cette réponse par rapport à la première voix et acceptée. Il a toutes les informations en un seul endroit (non voir les parties de ce lien) et a mentionné certaines spécificités Android dont je n'avais aucune idée (comme CREATE TABLE "android_metadata"). Des exemples sont également écrits en détail, ce qui est un plus. C'est presque une solution de copier-coller qui n'est pas toujours bonne mais les explications entre le code sont super.
Igor Čordaš
J'utilise la même méthode mais je suis confronté à un problème: comment copier plus facilement toutes les données existantes de l'ancien vers le nouveau fichier db.
Pankaj
130

La SQLiteAssetHelperbibliothèque rend cette tâche très simple.

Il est facile d'ajouter en tant que dépendance gradle (mais un Jar est également disponible pour Ant / Eclipse), et avec la documentation, il peut être trouvé à:
https://github.com/jgilfelt/android-sqlite-asset-helper

Remarque: Ce projet n'est plus maintenu comme indiqué sur le lien Github ci-dessus.

Comme expliqué dans la documentation:

  1. Ajoutez la dépendance au fichier de génération de gradle de votre module:

    dependencies {
        compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
    }
  2. Copiez la base de données dans le répertoire des ressources, dans un sous-répertoire appelé assets/databases. Par exemple:
    assets/databases/my_database.db

    (Facultativement, vous pouvez compresser la base de données dans un fichier zip tel que assets/databases/my_database.zip. Ce n'est pas nécessaire, car l'APK est déjà compressé dans son ensemble.)

  3. Créez une classe, par exemple:

    public class MyDatabase extends SQLiteAssetHelper {
    
        private static final String DATABASE_NAME = "my_database.db";
        private static final int DATABASE_VERSION = 1;
    
        public MyDatabase(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
    }
DavidEG
la source
Le téléchargement android-sqlite-asset-helper.jar nécessite quelles informations d'identification?
Pr38y
1
Si vous utilisez gradle, vous ajoutez simplement la dépendance.
Suragch
Comment obtenez-vous les données de la base de données?
Machado
C'est encore plus facile avec Android Studio et Gradle. Vérifiez le lien!
bendaf
5
Notez que cette bibliothèque est abandonnée, avec la dernière mise à jour il y a 4 ans.
réduction de l'activité le
13

Ma solution n'utilise aucune bibliothèque tierce ni ne vous oblige à appeler des méthodes personnalisées sur la SQLiteOpenHelpersous-classe pour initialiser la base de données lors de la création. Il prend également en charge les mises à niveau de la base de données. Il suffit de sous-classer SQLiteOpenHelper.

Prérequis:

  1. La base de données que vous souhaitez expédier avec l'application. Il doit contenir une table 1x1 nommée android_metadataavec un attribut localeayant la valeur en_USen plus des tables uniques à votre application.

Sous SQLiteOpenHelper- classement :

  1. Sous-classe SQLiteOpenHelper.
  2. Créez une privateméthode dans la SQLiteOpenHelpersous - classe. Cette méthode contient la logique pour copier le contenu de la base de données du fichier de base de données dans le dossier «assets» vers la base de données créée dans le contexte du package d'application.
  3. Remplacer onCreate, onUpgrade et onOpen les méthodes de SQLiteOpenHelper.

Assez dit. Voici la SQLiteOpenHelpersous - classe:

public class PlanDetailsSQLiteOpenHelper extends SQLiteOpenHelper {
    private static final String TAG = "SQLiteOpenHelper";

    private final Context context;
    private static final int DATABASE_VERSION = 1;
    private static final String DATABASE_NAME = "my_custom_db";

    private boolean createDb = false, upgradeDb = false;

    public PlanDetailsSQLiteOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

    /**
     * Copy packaged database from assets folder to the database created in the
     * application package context.
     * 
     * @param db
     *            The target database in the application package context.
     */
    private void copyDatabaseFromAssets(SQLiteDatabase db) {
        Log.i(TAG, "copyDatabase");
        InputStream myInput = null;
        OutputStream myOutput = null;
        try {
            // Open db packaged as asset as the input stream
            myInput = context.getAssets().open("path/to/shipped/db/file");

            // Open the db in the application package context:
            myOutput = new FileOutputStream(db.getPath());

            // Transfer db file contents:
            byte[] buffer = new byte[1024];
            int length;
            while ((length = myInput.read(buffer)) > 0) {
                myOutput.write(buffer, 0, length);
            }
            myOutput.flush();

            // Set the version of the copied database to the current
            // version:
            SQLiteDatabase copiedDb = context.openOrCreateDatabase(
                DATABASE_NAME, 0, null);
            copiedDb.execSQL("PRAGMA user_version = " + DATABASE_VERSION);
            copiedDb.close();

        } catch (IOException e) {
            e.printStackTrace();
            throw new Error(TAG + " Error copying database");
        } finally {
            // Close the streams
            try {
                if (myOutput != null) {
                    myOutput.close();
                }
                if (myInput != null) {
                    myInput.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                throw new Error(TAG + " Error closing streams");
            }
        }
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.i(TAG, "onCreate db");
        createDb = true;
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.i(TAG, "onUpgrade db");
        upgradeDb = true;
    }

    @Override
    public void onOpen(SQLiteDatabase db) {
        Log.i(TAG, "onOpen db");
        if (createDb) {// The db in the application package
            // context is being created.
            // So copy the contents from the db
            // file packaged in the assets
            // folder:
            createDb = false;
            copyDatabaseFromAssets(db);

        }
        if (upgradeDb) {// The db in the application package
            // context is being upgraded from a lower to a higher version.
            upgradeDb = false;
            // Your db upgrade logic here:
        }
    }
}

Enfin, pour obtenir une connexion à la base de données, il suffit d'appeler getReadableDatabase()ou getWritableDatabase()sur la SQLiteOpenHelpersous - classe et il se chargera de créer une base de données, en copiant le contenu de la base de données à partir du fichier spécifié dans le dossier 'assets', si la base de données n'existe pas.

En bref, vous pouvez utiliser la SQLiteOpenHelpersous - classe pour accéder à la base de données livrée dans le dossier des ressources comme vous le feriez pour une base de données initialisée à l'aide de requêtes SQL dans la onCreate()méthode.

Vaishak Nair
la source
2
Il s'agit de la solution la plus élégante, utilisant des API Androids standard sans avoir besoin de bibliothèques externes. Remarque: je n'ai pas inclus la table android_metadata et cela fonctionne, les nouvelles versions d'Android pourraient l'ajouter automatiquement.
goetzc
12

Expédition de l'application avec un fichier de base de données, dans Android Studio 3.0

L'expédition de l'application avec un fichier de base de données est une bonne idée pour moi. L'avantage est que vous n'avez pas besoin de faire une initialisation complexe, ce qui coûte parfois beaucoup de temps, si votre ensemble de données est énorme.

Étape 1: préparer le fichier de base de données

Préparez votre fichier de base de données. Il peut s'agir d'un fichier .db ou d'un fichier .sqlite. Si vous utilisez un fichier .sqlite, tout ce que vous devez faire est de modifier les noms d'extension de fichier. Les étapes sont les mêmes.

Dans cet exemple, j'ai préparé un fichier appelé testDB.db. Il a une table et quelques exemples de données comme celui-ci entrez la description de l'image ici

Étape 2: importez le fichier dans votre projet

Créez le dossier des ressources si vous n'en avez pas. Copiez et collez ensuite le fichier de base de données dans ce dossier

entrez la description de l'image ici

Étape 3: copiez le fichier dans le dossier de données de l'application

Vous devez copier le fichier de base de données dans le dossier de données de l'application afin de poursuivre l'interaction avec celui-ci. Il s'agit d'une action unique (initialisation) pour copier le fichier de base de données. Si vous appelez ce code plusieurs fois, le fichier de base de données du dossier de données sera écrasé par celui du dossier d'actifs. Ce processus de remplacement est utile lorsque vous souhaitez mettre à jour la base de données à l'avenir lors de la mise à jour de l'application.

Notez que lors de la mise à jour de l'application, ce fichier de base de données ne sera pas modifié dans le dossier de données de l'application. Seule la désinstallation la supprimera.

Le fichier de base de données doit être copié dans le /databasesdossier. Ouvrez l'Explorateur de fichiers de périphérique. Entrez l' data/data/<YourAppName>/emplacement. Il s'agit du dossier de données par défaut de l'application mentionné ci-dessus. Et par défaut, le fichier de base de données sera placé dans un autre dossier appelé bases de données sous ce répertoire

entrez la description de l'image ici

Maintenant, le processus de copie des fichiers ressemble à peu près à ce que fait Java. Utilisez le code suivant pour faire le copier-coller. Ceci est le code d'initiation. Il peut également être utilisé pour mettre à jour (par écrasement) le fichier de base de données à l'avenir.

//get context by calling "this" in activity or getActivity() in fragment
//call this if API level is lower than 17  String appDataPath = "/data/data/" + context.getPackageName() + "/databases/"
String appDataPath = context.getApplicationInfo().dataDir;

File dbFolder = new File(appDataPath + "/databases");//Make sure the /databases folder exists
dbFolder.mkdir();//This can be called multiple times.

File dbFilePath = new File(appDataPath + "/databases/testDB.db");

try {
    InputStream inputStream = context.getAssets().open("testDB.db");
    OutputStream outputStream = new FileOutputStream(dbFilePath);
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer))>0)
    {
        outputStream.write(buffer, 0, length);
    }
    outputStream.flush();
    outputStream.close();
    inputStream.close();
} catch (IOException e){
    //handle
}

Actualisez ensuite le dossier pour vérifier le processus de copie

entrez la description de l'image ici

Étape 4: créer un assistant d'ouverture de base de données

Créer une sous-classe pour SQLiteOpenHelper, avec connect, close, path, etc. Je l'ai nomméeDatabaseOpenHelper

import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseOpenHelper extends SQLiteOpenHelper {
    public static final String DB_NAME = "testDB.db";
    public static final String DB_SUB_PATH = "/databases/" + DB_NAME;
    private static String APP_DATA_PATH = "";
    private SQLiteDatabase dataBase;
    private final Context context;

    public DatabaseOpenHelper(Context context){
        super(context, DB_NAME, null, 1);
        APP_DATA_PATH = context.getApplicationInfo().dataDir;
        this.context = context;
    }

    public boolean openDataBase() throws SQLException{
        String mPath = APP_DATA_PATH + DB_SUB_PATH;
        //Note that this method assumes that the db file is already copied in place
        dataBase = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.OPEN_READWRITE);
        return dataBase != null;
    }

    @Override
    public synchronized void close(){
        if(dataBase != null) {dataBase.close();}
        super.close();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

Étape 5: créer une classe de niveau supérieur pour interagir avec la base de données

Ce sera la classe qui lit et écrit votre fichier de base de données. Il existe également un exemple de requête pour imprimer la valeur dans la base de données.

import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

public class Database {
    private final Context context;
    private SQLiteDatabase database;
    private DatabaseOpenHelper dbHelper;

    public Database(Context context){
        this.context = context;
        dbHelper = new DatabaseOpenHelper(context);
    }

    public Database open() throws SQLException
    {
        dbHelper.openDataBase();
        dbHelper.close();
        database = dbHelper.getReadableDatabase();
        return this;
    }

    public void close()
    {
        dbHelper.close();
    }

    public void test(){
        try{
            String query ="SELECT value FROM test1";
            Cursor cursor = database.rawQuery(query, null);
            if (cursor.moveToFirst()){
                do{
                    String value = cursor.getString(0);
                    Log.d("db", value);
                }while (cursor.moveToNext());
            }
            cursor.close();
        } catch (SQLException e) {
            //handle
        }
    }
}

Étape 6: test en cours d'exécution

Testez le code en exécutant les lignes de codes suivantes.

Database db = new Database(context);
db.open();
db.test();
db.close();

Appuyez sur le bouton Exécuter et applaudissez!

entrez la description de l'image ici

Fangming
la source
1
quand l'initialisation doit-elle être effectuée? Quelle est la stratégie que vous proposez?
Daniele B
8

En novembre 2017, Google a publié la bibliothèque Room Persistence .

De la documentation:

La bibliothèque de persistance Chambre fournit une couche d'abstraction sur SQ solide texte Lite pour permettre l' accès de base de données couramment tout en tirant parti de la pleine puissance de SQLite .

La bibliothèque vous aide à créer un cache des données de votre application sur un appareil qui exécute votre application. Ce cache, qui constitue la seule source de vérité de votre application, permet aux utilisateurs d'afficher une copie cohérente des informations clés dans votre application, que les utilisateurs disposent ou non d'une connexion Internet.

La base de données Room a un rappel lorsque la base de données est créée ou ouverte pour la première fois. Vous pouvez utiliser le rappel de création pour remplir votre base de données.

Room.databaseBuilder(context.applicationContext,
        DataDatabase::class.java, "Sample.db")
        // prepopulate the database after onCreate was called
        .addCallback(object : Callback() {
            override fun onCreate(db: SupportSQLiteDatabase) {
                super.onCreate(db)
                // moving to a new thread
                ioThread {
                    getInstance(context).dataDao()
                                        .insert(PREPOPULATE_DATA)
                }
            }
        })
        .build()

Code de ce billet de blog .

LordRaydenMK
la source
Merci, cela a fonctionné pour moi. Exemple Java ici
Jerry Sha
1
Si vous souhaitez expédier un APK avec un SQLite déjà existant, vous pouvez l'ajouter au dossier d'actifs et utiliser ce package github.com/humazed/RoomAsset pour effectuer une migration qui chargera les données du fichier SQLite dans le nouveau. De cette façon, vous pouvez enregistrer le remplissage des données avec une base de données existante.
xarlymg89
6

D'après ce que j'ai vu, vous devriez expédier une base de données contenant déjà la configuration des tables et les données. Cependant, si vous le souhaitez (et selon le type d'application que vous avez), vous pouvez autoriser "l'option de mise à niveau de la base de données". Ensuite, vous devez télécharger la dernière version de sqlite, obtenir les dernières instructions Insert / Create d'un fichier texte hébergé en ligne, exécuter les instructions et effectuer un transfert de données de l'ancienne base de données vers la nouvelle.

masfenix
la source
6
> D'après ce que j'ai vu, vous devriez expédier une base de données contenant déjà la configuration des tables et les données. Oui mais comment tu fais ça?
Rory
5

Enfin je l'ai fait !! J'ai utilisé cette aide de lien Utilisation de votre propre base de données SQLite dans les applications Android , mais j'ai dû la changer un peu.

  1. Si vous avez plusieurs packages, vous devez mettre le nom du package principal ici:

    private static String DB_PATH = "data/data/masterPakageName/databases";

  2. J'ai changé la méthode qui copie la base de données du dossier local vers le dossier de l'émulateur! Il y avait un problème lorsque ce dossier n'existait pas. Donc, tout d'abord, il doit vérifier le chemin d'accès et s'il n'est pas là, il doit créer le dossier.

  3. Dans le code précédent, la copyDatabaseméthode n'a jamais été appelée lorsque la base de données n'existait pas et la checkDataBaseméthode a provoqué une exception. j'ai donc changé un peu le code.

  4. Si votre base de données n'a pas d'extension de fichier, n'utilisez pas le nom de fichier avec une seule.

ça marche bien pour moi, j'espère que ça vous sera utile aussi

    package farhangsarasIntroduction;


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;

import android.content.Context;
import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;

import android.util.Log;


    public class DataBaseHelper extends SQLiteOpenHelper{

    //The Android's default system path of your application database.
    private static String DB_PATH = "data/data/com.example.sample/databases";

    private static String DB_NAME = "farhangsaraDb";

    private SQLiteDatabase myDataBase;

    private final Context myContext;

    /**
      * Constructor
      * Takes and keeps a reference of the passed context in order to access to the application assets and resources.
      * @param context
      */
    public DataBaseHelper(Context context) {

        super(context, DB_NAME, null, 1);
            this.myContext = context;

    }   

    /**
      * Creates a empty database on the system and rewrites it with your own database.
      * */
    public void createDataBase() {

        boolean dbExist;
        try {

             dbExist = checkDataBase();


        } catch (SQLiteException e) {

            e.printStackTrace();
            throw new Error("database dose not exist");

        }

        if(dbExist){
        //do nothing - database already exist
        }else{

            try {

                copyDataBase();


            } catch (IOException e) {

                e.printStackTrace();
                throw new Error("Error copying database");

            }
    //By calling this method and empty database will be created into the default system path
    //of your application so we are gonna be able to overwrite that database with our database.
        this.getReadableDatabase();


    }

    }

    /**
      * Check if the database already exist to avoid re-copying the file each time you open the application.
      * @return true if it exists, false if it doesn't
      */
    private boolean checkDataBase(){

    SQLiteDatabase checkDB = null;

    try{
        String myPath = DB_PATH +"/"+ DB_NAME;

        checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
    }catch(SQLiteException e){

    //database does't exist yet.
        throw new Error("database does't exist yet.");

    }

    if(checkDB != null){

    checkDB.close();

    }

    return checkDB != null ? true : false;
    }

    /**
      * Copies your database from your local assets-folder to the just created empty database in the
      * system folder, from where it can be accessed and handled.
      * This is done by transfering bytestream.
      * */
    private void copyDataBase() throws IOException{



            //copyDataBase();
            //Open your local db as the input stream
            InputStream myInput = myContext.getAssets().open(DB_NAME);

            // Path to the just created empty db
            String outFileName = DB_PATH +"/"+ DB_NAME;
            File databaseFile = new File( DB_PATH);
             // check if databases folder exists, if not create one and its subfolders
            if (!databaseFile.exists()){
                databaseFile.mkdir();
            }

            //Open the empty db as the output stream
            OutputStream myOutput = new FileOutputStream(outFileName);

            //transfer bytes from the inputfile to the outputfile
            byte[] buffer = new byte[1024];
            int length;
            while ((length = myInput.read(buffer))>0){
            myOutput.write(buffer, 0, length);
            }

            //Close the streams
            myOutput.flush();
            myOutput.close();
            myInput.close();



    }



    @Override
    public synchronized void close() {

        if(myDataBase != null)
        myDataBase.close();

        super.close();

    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }



    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

     you to create adapters for your views.

}
afsane
la source
pouvez-vous s'il vous plaît laissez-moi savoir comment mettre à niveau la base de données si je veux remplacer l'ancienne base de données par une nouvelle, comment puis-je supprimer l'ancienne base de données
Erum
Je n'ai pas besoin de le faire jusqu'à présent, mais si une nouvelle application a été installée, la nouvelle db remplace également
afsane
comment supprimer l'ancienne base de données parce que j'ajoute une nouvelle base de données dans le dossier des actifs, puis comment supprimer l'ancienne base de données du dossier spécifié, sinon elle apportera le contenu de l'ancienne base de données
Erum
J'espère que ce serait utile stackoverflow.com/questions/9109438/…
afsane
Parfait, merci! Juste un commentaire, lever l'exception lors de la vérification de la base de données provoque la fermeture de l'application, car la base de données ne sera pas là au début et la méthode ne continuera pas après que l'exception soit levée. J'ai simplement commenté la nouvelle erreur ("la dose de base de données n'existe pas"); et maintenant tout fonctionne parfaitement.
Grinner
4

Actuellement, il n'y a aucun moyen de pré-créer une base de données SQLite à livrer avec votre apk. Le mieux que vous puissiez faire est d'enregistrer le SQL approprié en tant que ressource et de les exécuter à partir de votre application. Oui, cela conduit à la duplication des données (les mêmes informations existent en tant que resrouce et en tant que base de données) mais il n'y a pas d'autre moyen pour le moment. Le seul facteur atténuant est que le fichier apk est compressé. Mon expérience est de 908 Ko compresse à moins de 268 Ko.

Le fil ci-dessous a la meilleure discussion / solution que j'ai trouvée avec un bon exemple de code.

http://groups.google.com/group/android-developers/msg/9f455ae93a1cf152

J'ai stocké mon instruction CREATE en tant que ressource de chaîne à lire avec Context.getString () et l'ai exécutée avec SQLiteDatabse.execSQL ().

J'ai stocké les données de mes insertions dans res / raw / inserts.sql (j'ai créé le fichier sql, 7000+ lignes). En utilisant la technique du lien ci-dessus, j'ai entré une boucle, lu le fichier ligne par ligne et concaténé les données dans "INSERT INTO tbl VALUE" et j'ai fait une autre SQLiteDatabase.execSQL (). Cela n'a aucun sens d'enregistrer 7000 "INSERT INTO tbl VALUE" quand ils peuvent être simplement concacténés.

Cela prend environ vingt secondes sur l'émulateur, je ne sais pas combien de temps cela prendrait sur un vrai téléphone, mais cela ne se produit qu'une seule fois, lorsque l'utilisateur démarre l'application pour la première fois.

Volonté
la source
3
Que diriez-vous de retirer le script SQL du Web lors de la première exécution? De cette façon, il n'est pas nécessaire de dupliquer les données.
Tamas Czinege
1
Oui, mais l'appareil devra être connecté à Internet. C'est un sérieux inconvénient dans certaines applications.
Dzhuneyt du
Ne faites pas 7000+ insertions, faites des insertions par lots de 100 ou plus comme ceci - INSERT INTO table VALUES(...) VALUES(...) VALUES(...) ...(1 ligne d'insertion devrait avoir 100 VALEURS). Il sera beaucoup plus efficace et réduira votre temps de démarrage de 20 à 2 ou 3 secondes.
Mohit Atray
4

L'expédition de la base de données dans l'apk, puis sa copie /data/data/...doublera la taille de la base de données (1 en apk, 1 en data/data/...) et augmentera la taille de l'apk (bien sûr). Votre base de données ne doit donc pas être trop volumineuse.

Hiep
la source
2
Cela augmente quelque peu la taille de l'apk mais ne la double pas. Lorsqu'il est dans les actifs, il est compressé et donc beaucoup plus petit. Après l'avoir copié dans le dossier de la base de données, il n'est pas compressé.
Suragch
3

Android propose déjà une approche de gestion de base de données basée sur les versions. Cette approche a été mise à profit dans le cadre BARACUS pour les applications Android.

Il vous permet de gérer la base de données tout au long du cycle de vie de la version d'une application, étant en mesure de mettre à jour la base de données sqlite de n'importe quelle version précédente à la version actuelle.

En outre, il vous permet d'exécuter des sauvegardes à chaud et une récupération à chaud du SQLite.

Je ne suis pas sûr à 100%, mais une récupération à chaud pour un appareil spécifique peut vous permettre d'envoyer une base de données préparée dans votre application. Mais je ne suis pas sûr du format binaire de la base de données qui pourrait être spécifique à certains appareils, fournisseurs ou générations d'appareils.

Puisque le truc est Apache License 2, n'hésitez pas à réutiliser n'importe quelle partie du code, qui peut être trouvé sur github

ÉDITER :

Si vous souhaitez uniquement envoyer des données, vous pouvez envisager d'instancier et de conserver les POJO au premier démarrage des applications. BARACUS a un support intégré à cela (magasin de valeurs de clé intégré pour les informations de configuration, par exemple "APP_FIRST_RUN" plus un hook après-contexte-bootstrap afin d'exécuter des opérations post-lancement sur le contexte). Cela vous permet d'avoir des données couplées étroitement livrées avec votre application; dans la plupart des cas, cela correspondait à mes cas d'utilisation.

gorefest
la source
3

Si les données requises ne sont pas trop grandes (les limites que je ne connais pas, cela dépend de beaucoup de choses), vous pouvez également télécharger les données (en XML, JSON, peu importe) à partir d'un site Web / d'une application Web. Après la réception, exécutez les instructions SQL en utilisant les données reçues en créant vos tables et en insérant les données.

Si votre application mobile contient de nombreuses données, il peut être plus facile de mettre à jour les données des applications installées avec des données ou des modifications plus précises.

Jaco
la source
3

J'ai modifié la classe et les réponses à la question et écrit une classe qui permet de mettre à jour la base de données via DB_VERSION.

public class DatabaseHelper extends SQLiteOpenHelper {
    private static String DB_NAME = "info.db";
    private static String DB_PATH = "";
    private static final int DB_VERSION = 1;

    private SQLiteDatabase mDataBase;
    private final Context mContext;
    private boolean mNeedUpdate = false;

    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        if (android.os.Build.VERSION.SDK_INT >= 17)
            DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
        else
            DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
        this.mContext = context;

        copyDataBase();

        this.getReadableDatabase();
    }

    public void updateDataBase() throws IOException {
        if (mNeedUpdate) {
            File dbFile = new File(DB_PATH + DB_NAME);
            if (dbFile.exists())
                dbFile.delete();

            copyDataBase();

            mNeedUpdate = false;
        }
    }

    private boolean checkDataBase() {
        File dbFile = new File(DB_PATH + DB_NAME);
        return dbFile.exists();
    }

    private void copyDataBase() {
        if (!checkDataBase()) {
            this.getReadableDatabase();
            this.close();
            try {
                copyDBFile();
            } catch (IOException mIOException) {
                throw new Error("ErrorCopyingDataBase");
            }
        }
    }

    private void copyDBFile() throws IOException {
        InputStream mInput = mContext.getAssets().open(DB_NAME);
        //InputStream mInput = mContext.getResources().openRawResource(R.raw.info);
        OutputStream mOutput = new FileOutputStream(DB_PATH + DB_NAME);
        byte[] mBuffer = new byte[1024];
        int mLength;
        while ((mLength = mInput.read(mBuffer)) > 0)
            mOutput.write(mBuffer, 0, mLength);
        mOutput.flush();
        mOutput.close();
        mInput.close();
    }

    public boolean openDataBase() throws SQLException {
        mDataBase = SQLiteDatabase.openDatabase(DB_PATH + DB_NAME, null, SQLiteDatabase.CREATE_IF_NECESSARY);
        return mDataBase != null;
    }

    @Override
    public synchronized void close() {
        if (mDataBase != null)
            mDataBase.close();
        super.close();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (newVersion > oldVersion)
            mNeedUpdate = true;
    }
}

Utiliser une classe.

Dans la classe d'activité, déclarez les variables.

private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;

Dans la méthode onCreate, écrivez le code suivant.

mDBHelper = new DatabaseHelper(this);

try {
    mDBHelper.updateDataBase();
} catch (IOException mIOException) {
    throw new Error("UnableToUpdateDatabase");
}

try {
    mDb = mDBHelper.getWritableDatabase();
} catch (SQLException mSQLException) {
    throw mSQLException;
}

Si vous ajoutez un fichier de base de données au dossier res / raw, utilisez la modification suivante de la classe.

public class DatabaseHelper extends SQLiteOpenHelper {
    private static String DB_NAME = "info.db";
    private static String DB_PATH = "";
    private static final int DB_VERSION = 1;

    private SQLiteDatabase mDataBase;
    private final Context mContext;
    private boolean mNeedUpdate = false;

    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        if (android.os.Build.VERSION.SDK_INT >= 17)
            DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
        else
            DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
        this.mContext = context;

        copyDataBase();

        this.getReadableDatabase();
    }

    public void updateDataBase() throws IOException {
        if (mNeedUpdate) {
            File dbFile = new File(DB_PATH + DB_NAME);
            if (dbFile.exists())
                dbFile.delete();

            copyDataBase();

            mNeedUpdate = false;
        }
    }

    private boolean checkDataBase() {
        File dbFile = new File(DB_PATH + DB_NAME);
        return dbFile.exists();
    }

    private void copyDataBase() {
        if (!checkDataBase()) {
            this.getReadableDatabase();
            this.close();
            try {
                copyDBFile();
            } catch (IOException mIOException) {
                throw new Error("ErrorCopyingDataBase");
            }
        }
    }

    private void copyDBFile() throws IOException {
        //InputStream mInput = mContext.getAssets().open(DB_NAME);
        InputStream mInput = mContext.getResources().openRawResource(R.raw.info);
        OutputStream mOutput = new FileOutputStream(DB_PATH + DB_NAME);
        byte[] mBuffer = new byte[1024];
        int mLength;
        while ((mLength = mInput.read(mBuffer)) > 0)
            mOutput.write(mBuffer, 0, mLength);
        mOutput.flush();
        mOutput.close();
        mInput.close();
    }

    public boolean openDataBase() throws SQLException {
        mDataBase = SQLiteDatabase.openDatabase(DB_PATH + DB_NAME, null, SQLiteDatabase.CREATE_IF_NECESSARY);
        return mDataBase != null;
    }

    @Override
    public synchronized void close() {
        if (mDataBase != null)
            mDataBase.close();
        super.close();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (newVersion > oldVersion)
            mNeedUpdate = true;
    }
}

http://blog.harrix.org/article/6784

Harrix
la source
2

J'ai écrit une bibliothèque pour simplifier ce processus.

dataBase = new DataBase.Builder(context, "myDb").
//        setAssetsPath(). // default "databases"
//        setDatabaseErrorHandler().
//        setCursorFactory().
//        setUpgradeCallback()
//        setVersion(). // default 1
build();

Il créera une base de données à partir d'un assets/databases/myDb.dbfichier. De plus, vous obtiendrez toutes ces fonctionnalités:

  • Charger la base de données à partir du fichier
  • Accès synchronisé à la base de données
  • Utilisation de sqlite-android par requery, distribution spécifique à Android des dernières versions de SQLite.

Clonez-le depuis github .

Ilya Gazman
la source
2

J'utilise ORMLite et le code ci-dessous a fonctionné pour moi

public class DatabaseProvider extends OrmLiteSqliteOpenHelper {
    private static final String DatabaseName = "DatabaseName";
    private static final int DatabaseVersion = 1;
    private final Context ProvidedContext;

    public DatabaseProvider(Context context) {
        super(context, DatabaseName, null, DatabaseVersion);
        this.ProvidedContext= context;
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        boolean databaseCopied = preferences.getBoolean("DatabaseCopied", false);
        if (databaseCopied) {
            //Do Nothing
        } else {
            CopyDatabase();
            SharedPreferences.Editor editor = preferences.edit();
            editor.putBoolean("DatabaseCopied", true);
            editor.commit();
        }
    }

    private String DatabasePath() {
        return "/data/data/" + ProvidedContext.getPackageName() + "/databases/";
    }

    private void CopyDatabase() {
        try {
            CopyDatabaseInternal();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private File ExtractAssetsZip(String zipFileName) {
        InputStream inputStream;
        ZipInputStream zipInputStream;
        File tempFolder;
        do {
            tempFolder = null;
            tempFolder = new File(ProvidedContext.getCacheDir() + "/extracted-" + System.currentTimeMillis() + "/");
        } while (tempFolder.exists());

        tempFolder.mkdirs();

        try {
            String filename;
            inputStream = ProvidedContext.getAssets().open(zipFileName);
            zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));
            ZipEntry zipEntry;
            byte[] buffer = new byte[1024];
            int count;

            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                filename = zipEntry.getName();
                if (zipEntry.isDirectory()) {
                    File fmd = new File(tempFolder.getAbsolutePath() + "/" + filename);
                    fmd.mkdirs();
                    continue;
                }

                FileOutputStream fileOutputStream = new FileOutputStream(tempFolder.getAbsolutePath() + "/" + filename);
                while ((count = zipInputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, count);
                }

                fileOutputStream.close();
                zipInputStream.closeEntry();
            }

            zipInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        return tempFolder;
    }

    private void CopyDatabaseInternal() throws IOException {

        File extractedPath = ExtractAssetsZip(DatabaseName + ".zip");
        String databaseFile = "";
        for (File innerFile : extractedPath.listFiles()) {
            databaseFile = innerFile.getAbsolutePath();
            break;
        }
        if (databaseFile == null || databaseFile.length() ==0 )
            throw new RuntimeException("databaseFile is empty");

        InputStream inputStream = new FileInputStream(databaseFile);

        String outFileName = DatabasePath() + DatabaseName;

        File destinationPath = new File(DatabasePath());
        if (!destinationPath.exists())
            destinationPath.mkdirs();

        File destinationFile = new File(outFileName);
        if (!destinationFile.exists())
            destinationFile.createNewFile();

        OutputStream myOutput = new FileOutputStream(outFileName);

        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }

        myOutput.flush();
        myOutput.close();
        inputStream.close();
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource, int fromVersion, int toVersion) {

    }
}

Veuillez noter que le code extrait le fichier de base de données d'un fichier zip dans les actifs

Homayoun Behzadian
la source