Versenden Sie eine Anwendung mit einer Datenbank


959

Wenn Ihre Anwendung eine Datenbank benötigt und integrierte Daten enthält, wie kann diese Anwendung am besten ausgeliefert werden? Sollte ich:

  1. Erstellen Sie die SQLite-Datenbank vor und fügen Sie sie in das .apk?

  2. Fügen Sie die SQL-Befehle in die Anwendung ein und lassen Sie sie die Datenbank erstellen und die Daten bei der ersten Verwendung einfügen?

Die Nachteile, die ich sehe, sind:

  1. Mögliche Nichtübereinstimmungen der SQLite-Version können Probleme verursachen, und ich weiß derzeit nicht, wohin die Datenbank gehen soll und wie ich darauf zugreifen soll.

  2. Das Erstellen und Auffüllen der Datenbank auf dem Gerät kann sehr lange dauern.

Irgendwelche Vorschläge? Hinweise auf die Dokumentation zu Problemen sind sehr willkommen.



Antworten:


199

Es gibt zwei Möglichkeiten zum Erstellen und Aktualisieren von Datenbanken.

Eine besteht darin, eine Datenbank extern zu erstellen, sie dann im Assets-Ordner des Projekts abzulegen und dann die gesamte Datenbank von dort zu kopieren. Dies ist viel schneller, wenn die Datenbank viele Tabellen und andere Komponenten enthält. SQLite Manager FireFox-Plugin zum Ausführen Ihrer SQL-Erstellungsanweisungen.Upgrades werden durch Ändern der Datenbankversionsnummer in der Datei res / values ​​/ strings.xml ausgelöst. Upgrades werden dann durchgeführt, indem eine neue Datenbank extern erstellt, die alte Datenbank im Assets-Ordner durch die neue Datenbank ersetzt, die alte Datenbank im internen Speicher unter einem anderen Namen gespeichert, die neue Datenbank aus dem Assets-Ordner in den internen Speicher kopiert und alle übertragen werden der Daten aus der alten Datenbank (die zuvor umbenannt wurde) in die neue Datenbank und schließlich das Löschen der alten Datenbank. Sie können eine Datenbank ursprünglich mithilfe von erstellen

Die andere Möglichkeit besteht darin, eine Datenbank intern aus einer SQL-Datei zu erstellen. Dies ist nicht so schnell, aber die Verzögerung wäre für die Benutzer wahrscheinlich nicht wahrnehmbar, wenn die Datenbank nur wenige Tabellen enthält. Upgrades werden durch Ändern der Datenbankversionsnummer in der Datei res / values ​​/ strings.xml ausgelöst. Upgrades würden dann durch Verarbeiten einer Upgrade-SQL-Datei durchgeführt. Die Daten in der Datenbank bleiben unverändert, außer wenn der Container entfernt wird, z. B. wenn eine Tabelle gelöscht wird.

Das folgende Beispiel zeigt, wie beide Methoden verwendet werden.

Hier ist ein Beispiel für die Datei create_database.sql. Es muss im Assets-Ordner des Projekts für die interne Methode abgelegt oder in "SQL ausführen" von SQLite Manager kopiert werden, um die Datenbank für die externe Methode zu erstellen. (HINWEIS: Beachten Sie den Kommentar zu der von Android benötigten Tabelle.)

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

Hier ist eine Beispieldatei update_database.sql. Es muss im Assets-Ordner des Projekts für die interne Methode abgelegt oder in "SQL ausführen" von SQLite Manager kopiert werden, um die Datenbank für die externe Methode zu erstellen. (HINWEIS: Beachten Sie, dass alle drei Arten von SQL-Kommentaren ignoriert werden durch den in diesem Beispiel enthaltenen SQL-Parser.)

--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');

Hier ist ein Eintrag, der der Datei /res/values/strings.xml für die Datenbankversionsnummer hinzugefügt werden kann.

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

Hier ist eine Aktivität, die auf die Datenbank zugreift und diese dann verwendet. ( Hinweis: Möglicherweise möchten Sie den Datenbankcode in einem separaten Thread ausführen, wenn er viele Ressourcen verwendet. )

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

    }
}

Hier ist die Datenbank-Hilfsklasse, in der die Datenbank bei Bedarf erstellt oder aktualisiert wird. (HINWEIS: Für Android müssen Sie eine Klasse erstellen, die SQLiteOpenHelper erweitert, um mit einer SQLite-Datenbank arbeiten zu können.)

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.
     */

}

Hier ist die FileHelper-Klasse, die Methoden zum Kopieren von Byte-Stream-Dateien und zum Parsen von SQL-Dateien enthält.

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(";");
    }

}

Ich habe den obigen Code verwendet, um meine Datenbank zu aktualisieren. "upgrade_database.sql enthält insert-Anweisung. Einige der Werte haben Semicoln wie Einfügen in table_a-Werte ('ss', 'ddd', 'aaaa; aaa');", wenn ich die ausführe Ich bemerkte, dass die oben erwähnte Einfügung aufgrund des Semicolns in den Werten nicht sicher ist, wie dies behoben werden kann.
Sam

5
Es gibt eine dritte Option: Kopieren Sie die Datenbank aus dem Web. Ich habe das getan und es geht ziemlich schnell für eine 4-Meg-Datenbank. Es löst auch das Problem mit 2.3, für das die erste Lösung (Kopie db) nicht funktioniert.
Jack BeNimble

2
Danny und Austyn - Ihre Lösung war perfekt. Ich hatte Probleme mit meiner selbst gebrauten Lösung und bin auf Ihre gestoßen. Es war wirklich genau das Richtige. Vielen Dank, dass Sie sich die Zeit genommen haben, es bereitzustellen.
George Baker

4
Ich bevorzuge diese Antwort gegenüber der am besten gewählten und akzeptierten. Es hat alle Informationen an einem Ort (siehe diese Link-Teile nicht) und erwähnte einige Android-Besonderheiten, von denen ich keine Ahnung hatte, dass sie existieren (wie CREATE TABLE "android_metadata"). Auch Beispiele sind sehr detailliert geschrieben, was ein Plus ist. Es ist fast eine Copy-Paste-Lösung, die nicht immer gut ist, aber die Erklärungen zwischen den Codes sind großartig.
Igor Čordaš

Ich verwende dieselbe Methode, habe jedoch ein Problem. So können wir alle vorhandenen Daten einfacher von der alten in die neue Datenbankdatei kopieren.
Pankaj

130

Die SQLiteAssetHelperBibliothek macht diese Aufgabe wirklich einfach.

Es ist einfach, es als Gradle-Abhängigkeit hinzuzufügen (aber ein Jar ist auch für Ant / Eclipse verfügbar). Zusammen mit der Dokumentation finden Sie es unter:
https://github.com/jgilfelt/android-sqlite-asset-helper

Hinweis: Dieses Projekt wird nicht mehr wie oben unter Github-Link angegeben gepflegt.

Wie in der Dokumentation erläutert:

  1. Fügen Sie die Abhängigkeit zur Gradle-Build-Datei Ihres Moduls hinzu:

    dependencies {
        compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
    }
  2. Kopieren Sie die Datenbank in das Assets-Verzeichnis in einem Unterverzeichnis namens assets/databases. Zum Beispiel:
    assets/databases/my_database.db

    (Optional können Sie die Datenbank in einer Zip-Datei wie z assets/databases/my_database.zip . Dies ist nicht erforderlich, da die APK bereits als Ganzes komprimiert ist.)

  3. Erstellen Sie eine Klasse, zum Beispiel:

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

Für das Herunterladen von android-sqlite-asset-helper.jar ist welcher Berechtigungsnachweis erforderlich?
Pr38y

1
Wenn Sie gradle verwenden, fügen Sie einfach die Abhängigkeit hinzu.
Suragch

Wie erhält man Daten aus der DB?
Machado

Mit Android Studio und gradle ist es noch einfacher. Überprüfen Sie den Link!
Bendaf

5
Beachten Sie, dass diese Bibliothek mit dem letzten Update vor 4 Jahren aufgegeben wurde.
Reduzierung der Aktivität

13

Meine Lösung verwendet weder eine Bibliothek eines Drittanbieters noch zwingt sie Sie, benutzerdefinierte Methoden für die SQLiteOpenHelperUnterklasse aufzurufen , um die Datenbank bei der Erstellung zu initialisieren. Es kümmert sich auch um Datenbank-Upgrades. Alles, was getan werden muss, ist eine Unterklasse SQLiteOpenHelper.

Voraussetzung:

  1. Die Datenbank, die Sie mit der App versenden möchten. Es sollte eine 1x1-Tabelle android_metadatamit einem Attribut enthalten locale, das den Wert en_USzusätzlich zu den für Ihre App eindeutigen Tabellen enthält.

Unterklasse SQLiteOpenHelper:

  1. Unterklasse SQLiteOpenHelper.
  2. Erstellen Sie eine privateMethode innerhalb der SQLiteOpenHelperUnterklasse. Diese Methode enthält die Logik zum Kopieren von Datenbankinhalten aus der Datenbankdatei im Ordner "Assets" in die im Anwendungspaketkontext erstellte Datenbank.
  3. Außer Kraft setzen onCreate, onUpgrade und onOpen Methoden SQLiteOpenHelper.

Genug gesagt. Hier geht die SQLiteOpenHelperUnterklasse:

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

Um eine Datenbankverbindung herzustellen, rufen Sie einfach getReadableDatabase()oder getWritableDatabase()aufSQLiteOpenHelper Unterklasse auf und es wird , die den Datenbankinhalt aus der angegebenen Datei im Ordner "Assets" kopiert, wenn die Datenbank nicht vorhanden ist.

Kurz gesagt, Sie können die SQLiteOpenHelperUnterklasse verwenden, um auf die im Assets-Ordner gelieferte Datenbank zuzugreifen, genau wie Sie es für eine Datenbank verwenden würden, die mithilfe von SQL-Abfragen in der onCreate()Methode initialisiert wird.


2
Dies ist die eleganteste Lösung, bei der Standard-Androids-APIs ohne externe Bibliotheken verwendet werden. Als Hinweis habe ich die Tabelle android_metadata nicht eingefügt und sie funktioniert. Neuere Android-Versionen fügen sie möglicherweise automatisch hinzu.
Goetzc

12

Versand der App mit einer Datenbankdatei in Android Studio 3.0

Der Versand der App mit einer Datenbankdatei ist für mich eine gute Idee. Der Vorteil ist, dass Sie keine komplexe Initialisierung durchführen müssen, was manchmal viel Zeit kostet, wenn Ihr Datensatz sehr groß ist.

Schritt 1: Bereiten Sie die Datenbankdatei vor

Halten Sie Ihre Datenbankdatei bereit. Dies kann entweder eine DB-Datei oder eine SQLite-Datei sein. Wenn Sie eine .sqlite-Datei verwenden, müssen Sie lediglich die Namen der Dateierweiterungen ändern. Die Schritte sind die gleichen.

In diesem Beispiel habe ich eine Datei namens testDB.db vorbereitet. Es enthält eine Tabelle und einige Beispieldaten Geben Sie hier die Bildbeschreibung ein

Schritt 2: Importieren Sie die Datei in Ihr Projekt

Erstellen Sie den Assets-Ordner, falls Sie noch keinen hatten. Kopieren Sie dann die Datenbankdatei und fügen Sie sie in diesen Ordner ein

Geben Sie hier die Bildbeschreibung ein

Schritt 3: Kopieren Sie die Datei in den Datenordner der App

Sie müssen die Datenbankdatei in den Datenordner der App kopieren, um weitere Interaktionen mit ihr durchführen zu können. Dies ist eine einmalige Aktion (Initialisierung) zum Kopieren der Datenbankdatei. Wenn Sie diesen Code mehrmals aufrufen, wird die Datenbankdatei im Datenordner von der Datei im Assets-Ordner überschrieben. Dieser Überschreibvorgang ist nützlich, wenn Sie die Datenbank in Zukunft während des App-Updates aktualisieren möchten.

Beachten Sie, dass diese Datenbankdatei während des App-Updates nicht im Datenordner der App geändert wird. Nur durch Deinstallieren wird es gelöscht.

Die Datenbankdatei muss in einen /databasesOrdner kopiert werden . Öffnen Sie den Gerätedateie-Explorer. Ort eingeben data/data/<YourAppName>/. Dies ist der oben erwähnte Standarddatenordner der App. Standardmäßig wird die Datenbankdatei in einem anderen Ordner namens "Datenbanken" unter diesem Verzeichnis abgelegt

Geben Sie hier die Bildbeschreibung ein

Der Prozess zum Kopieren von Dateien ähnelt dem von Java. Verwenden Sie den folgenden Code, um das Kopieren und Einfügen durchzuführen. Dies ist der Initiationscode. Es kann auch verwendet werden, um die Datenbankdatei in Zukunft zu aktualisieren (durch Überschreiben).

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

Aktualisieren Sie dann den Ordner, um den Kopiervorgang zu überprüfen

Geben Sie hier die Bildbeschreibung ein

Schritt 4: Erstellen Sie einen Helfer zum Öffnen einer Datenbank

Erstellen Sie eine Unterklasse für SQLiteOpenHelper, mit connect, close, path usw. Ich habe sie benanntDatabaseOpenHelper

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) {
    }
}

Schritt 5: Erstellen Sie eine Klasse der obersten Ebene für die Interaktion mit der Datenbank

Dies ist die Klasse, die Ihre Datenbankdatei liest und schreibt. Außerdem gibt es eine Beispielabfrage, um den Wert in der Datenbank auszudrucken.

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

Schritt 6: Testlauf

Testen Sie den Code, indem Sie die folgenden Codezeilen ausführen.

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

Drücke den Run-Button und feuere an!

Geben Sie hier die Bildbeschreibung ein


1
Wann sollte die Initialisierung erfolgen? Welche Strategie schlagen Sie vor?
Daniele B

8

Im November 2017 veröffentlichte Google die Room Persistence Library .

Aus der Dokumentation:

Die Room-Persistenzbibliothek bietet eine Abstraktionsschicht über SQ Strong Text Lite, um einen fließenden Datenbankzugriff zu ermöglichen und gleichzeitig die volle Leistung von SQLite zu nutzen .

Mithilfe der Bibliothek können Sie einen Cache mit den Daten Ihrer App auf einem Gerät erstellen, auf dem Ihre App ausgeführt wird. Mit diesem Cache, der als einzige Wahrheitsquelle Ihrer App dient, können Benutzer eine konsistente Kopie der wichtigsten Informationen in Ihrer App anzeigen, unabhängig davon, ob Benutzer über eine Internetverbindung verfügen.

Die Raumdatenbank hat einen Rückruf, wenn die Datenbank zum ersten Mal erstellt oder geöffnet wird. Sie können den Rückruf erstellen verwenden, um Ihre Datenbank zu füllen.

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 aus diesem Blog-Beitrag .


Danke, das hat bei mir funktioniert. Java Beispiel hier
Jerry Sha

1
Wenn Sie ein APK mit einer bereits vorhandenen SQLite versenden möchten, können Sie es dem Assets-Ordner hinzufügen und mit diesem Paket github.com/humazed/RoomAsset eine Migration durchführen, bei der die SQLite-Dateidaten in die neue geladen werden. Auf diese Weise können Sie das Auffüllen von Daten mit einer vorhandenen Datenbank speichern.
Xarlymg89

6

Nach allem, was ich gesehen habe, sollten Sie eine Datenbank versenden, in der die Tabellen und Daten bereits eingerichtet sind. Wenn Sie möchten (und abhängig von der Art Ihrer Anwendung), können Sie jedoch die Option "Datenbank aktualisieren" zulassen. Anschließend laden Sie die neueste SQLite-Version herunter, rufen die neuesten Insert / Create-Anweisungen einer online gehosteten Textdatei ab, führen die Anweisungen aus und führen eine Datenübertragung von der alten zur neuen Datenbank durch.


6
> Soweit ich gesehen habe, sollten Sie eine Datenbank ausliefern, in der die Tabellen und Daten bereits eingerichtet sind. Ja, aber wie machst du das?
Rory

5

Endlich habe ich es gemacht!! Ich habe diese Link- Hilfe verwendet, um Ihre eigene SQLite-Datenbank in Android-Anwendungen zu verwenden , musste sie jedoch ein wenig ändern.

  1. Wenn Sie viele Pakete haben, sollten Sie hier den Namen des Master-Pakets eingeben:

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

  2. Ich habe die Methode geändert, mit der die Datenbank vom lokalen Ordner in den Emulatorordner kopiert wird! Es gab ein Problem, als dieser Ordner nicht existierte. Zunächst sollte der Pfad überprüft werden, und wenn er nicht vorhanden ist, sollte der Ordner erstellt werden.

  3. Im vorherigen Code wurde die copyDatabaseMethode nie aufgerufen, wenn die Datenbank nicht vorhanden war und die checkDataBaseMethode eine Ausnahme verursachte. Also habe ich den Code ein wenig geändert.

  4. Wenn Ihre Datenbank keine Dateierweiterung hat, verwenden Sie den Dateinamen nicht mit einer.

es funktioniert gut für mich, ich hoffe es wäre auch nützlich für dich

    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.

}


Ich muss das bis jetzt nicht tun, aber wenn eine neue App installiert wurde, ersetzt auch die neue
Datenbank

Wie lösche ich eine alte Datenbank, weil ich eine neue Datenbank im Assets-Ordner hinzufüge? Wie lösche ich dann eine alte Datenbank aus dem angegebenen Ordner
?

Ich hoffe, das wäre nützlich. Stackoverflow.com/questions/9109438/…
afsane

Perfekt, danke! Nur ein Kommentar, das Auslösen der Ausnahme beim Überprüfen der Datenbank, führt zum Schließen der App, da die Datenbank am Anfang nicht vorhanden ist und die Methode nach dem Auslösen der Ausnahme nicht fortgesetzt wird. Ich habe den neuen Fehler einfach auskommentiert ("Datenbankdosis nicht vorhanden"). und jetzt funktioniert alles perfekt.
Grinner

4

Derzeit gibt es keine Möglichkeit, eine SQLite-Datenbank für den Versand mit Ihrer apk vorab zu erstellen. Das Beste, was Sie tun können, ist, das entsprechende SQL als Ressource zu speichern und von Ihrer Anwendung aus auszuführen. Ja, dies führt zu doppelten Daten (dieselben Informationen existieren als Resrouce und als Datenbank), aber es gibt derzeit keinen anderen Weg. Der einzige mildernde Faktor ist, dass die APK-Datei komprimiert ist. Meine Erfahrung ist, dass 908 KB auf weniger als 268 KB komprimiert werden.

Der folgende Thread hat die beste Diskussion / Lösung, die ich mit gutem Beispielcode gefunden habe.

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

Ich habe meine CREATE-Anweisung als Zeichenfolgenressource gespeichert, die mit Context.getString () gelesen werden soll, und sie mit SQLiteDatabse.execSQL () ausgeführt.

Ich habe die Daten für meine Einfügungen in res / raw / inserts.sql gespeichert (ich habe die SQL-Datei mit mehr als 7000 Zeilen erstellt). Mit der Technik aus dem obigen Link habe ich eine Schleife eingegeben, die Datei Zeile für Zeile gelesen und die Daten auf "INSERT INTO tbl VALUE" zusammengefasst und eine weitere SQLiteDatabase.execSQL () ausgeführt. Es macht keinen Sinn, 7000 "INSERT IN tbl VALUE" zu speichern, wenn sie nur auf konzentriert werden können.

Auf dem Emulator dauert es ungefähr zwanzig Sekunden. Ich weiß nicht, wie lange dies auf einem echten Telefon dauern würde, aber es passiert nur einmal, wenn der Benutzer die Anwendung zum ersten Mal startet.


3
Wie wäre es, wenn Sie das SQL-Skript beim ersten Start aus dem Web ziehen? Auf diese Weise müssen keine Daten dupliziert werden.
Tamas Czinege

1
Ja, aber das Gerät muss mit dem Internet verbunden sein. Das ist ein schwerwiegender Nachteil bei einigen Apps.
Dzhuneyt

Machen Sie keine 7000+ Einfügungen, machen Sie keine Batch-Einfügungen von 100 oder so - INSERT INTO table VALUES(...) VALUES(...) VALUES(...) ...(1 Einfügungszeile sollte 100 WERTE haben). Es ist viel effizienter und reduziert Ihre Startzeit von 20 Sekunden auf 2 oder 3 Sekunden.
Mohit Atray

4

Wenn Sie die Datenbank innerhalb der apk versenden und dann in kopieren, /data/data/...wird die Größe der Datenbank verdoppelt (1 in apk, 1 in data/data/...) und die apk-Größe (natürlich) erhöht. Ihre Datenbank sollte also nicht zu groß sein.


2
Es erhöht die apk-Größe zwar etwas, verdoppelt sie aber nicht. Wenn es sich in Assets befindet, wird es komprimiert und ist daher viel kleiner. Nach dem Kopieren in den Datenbankordner wird es dekomprimiert.
Suragch

3

Android bietet bereits einen versionierungsorientierten Ansatz für die Datenbankverwaltung. Dieser Ansatz wurde im BARACUS-Framework für Android-Anwendungen genutzt.

Sie können die Datenbank über den gesamten Versionslebenszyklus einer App verwalten und so die SQLite-Datenbank von jeder früheren Version auf die aktuelle Version aktualisieren.

Außerdem können Sie Hot-Backups und Hot-Recovery von SQLite ausführen.

Ich bin nicht 100% sicher, aber eine Hot-Recovery für ein bestimmtes Gerät kann es Ihnen ermöglichen, eine vorbereitete Datenbank in Ihrer App zu versenden. Ich bin mir jedoch nicht sicher, welches Datenbank-Binärformat für bestimmte Geräte, Anbieter oder Gerätegenerationen spezifisch sein kann.

Da es sich um Apache License 2 handelt, können Sie jeden Teil des Codes, der auf github zu finden ist, wiederverwenden

BEARBEITEN:

Wenn Sie nur Daten versenden möchten, können Sie POJOs beim ersten Start der Anwendung instanziieren und beibehalten. BARACUS hat eine integrierte Unterstützung dafür (integrierter Schlüsselwertspeicher für Konfigurationsinfos, z. B. "APP_FIRST_RUN" plus einen Bootstrap-Hook nach dem Kontext, um Operationen nach dem Start für den Kontext auszuführen). Auf diese Weise können Sie eng gekoppelte Daten mit Ihrer App versenden lassen. In den meisten Fällen passte dies zu meinen Anwendungsfällen.


3

Wenn die erforderlichen Daten nicht zu groß sind (Grenzen, die ich nicht kenne, hängen von vielen Dingen ab), können Sie die Daten auch (in XML, JSON usw.) von einer Website / Webanwendung herunterladen. Führen Sie nach dem Empfang die SQL-Anweisungen mit den empfangenen Daten aus, erstellen Sie Ihre Tabellen und fügen Sie die Daten ein.

Wenn Ihre mobile App viele Daten enthält, ist es möglicherweise später einfacher, die Daten in den installierten Apps mit genaueren Daten oder Änderungen zu aktualisieren.


3

Ich habe die Klasse und die Antworten auf die Frage geändert und eine Klasse geschrieben, mit der die Datenbank über DB_VERSION aktualisiert werden kann.

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

Eine Klasse benutzen.

Deklarieren Sie in der Aktivitätsklasse Variablen.

private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;

Schreiben Sie in der onCreate-Methode den folgenden Code.

mDBHelper = new DatabaseHelper(this);

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

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

Wenn Sie dem Ordner res / raw eine Datenbankdatei hinzufügen, verwenden Sie die folgende Änderung der Klasse.

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


2

Ich habe eine Bibliothek geschrieben , um diesen Prozess zu vereinfachen.

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

Es wird eine Datenbank aus einer assets/databases/myDb.dbDatei erstellt. Darüber hinaus erhalten Sie alle diese Funktionen:

  • Laden Sie die Datenbank aus der Datei
  • Synchronisierter Zugriff auf die Datenbank
  • Unter Verwendung von SQLite -Android nach Bedarf, Android-spezifische Verteilung der neuesten Versionen von SQLite.

Klonen Sie es von Github .


2

Ich verwende ORMLite und der folgende Code hat für mich funktioniert

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) {

    }
}

Bitte beachten Sie, dass der Code die Datenbankdatei aus einer Zip-Datei in Assets extrahiert

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.