Spedisci un'applicazione con un database


959

Se l'applicazione richiede un database e viene fornito con dati integrati, qual è il modo migliore per spedire quell'applicazione? Dovrei:

  1. Precreare il database SQLite e includerlo nel .apk?

  2. Includere i comandi SQL con l'applicazione e farlo creare il database e inserire i dati al primo utilizzo?

Gli svantaggi che vedo sono:

  1. Eventuali disallineamenti della versione di SQLite potrebbero causare problemi e al momento non so dove dovrebbe andare il database e come accedervi.

  2. Potrebbe essere necessario molto tempo per creare e popolare il database sul dispositivo.

Eventuali suggerimenti? I puntatori alla documentazione riguardanti eventuali problemi sarebbero molto apprezzati.



Risposte:


199

Esistono due opzioni per la creazione e l'aggiornamento dei database.

Uno è quello di creare un database esternamente, quindi inserirlo nella cartella delle risorse del progetto e quindi copiare l'intero database da lì. Questo è molto più veloce se il database ha molte tabelle e altri componenti. Gli aggiornamenti vengono attivati ​​modificando il numero di versione del database nel file res / valori / strings.xml. Gli aggiornamenti sarebbero quindi realizzati creando un nuovo database esternamente, sostituendo il vecchio database nella cartella delle risorse con il nuovo database, salvando il vecchio database nella memoria interna con un altro nome, copiando il nuovo database dalla cartella delle risorse nella memoria interna, trasferendo tutti dei dati dal vecchio database (che è stato rinominato in precedenza) nel nuovo database e infine eliminare il vecchio database. È possibile creare un database in origine utilizzandoPlug-in FireFox di SQLite Manager per eseguire le tue istruzioni sql di creazione.

L'altra opzione è quella di creare un database internamente da un file sql. Questo non è così rapido ma il ritardo sarebbe probabilmente impercettibile per gli utenti se il database avesse solo poche tabelle. Gli aggiornamenti vengono attivati ​​modificando il numero di versione del database nel file res / valori / strings.xml. Gli aggiornamenti verrebbero quindi eseguiti elaborando un file sql di aggiornamento. I dati nel database rimarranno invariati tranne quando il suo contenitore viene rimosso, ad esempio eliminando una tabella.

L'esempio seguente mostra come utilizzare entrambi i metodi.

Ecco un esempio di file create_database.sql. Deve essere inserito nella cartella delle risorse del progetto per il metodo interno o copiato in "Esegui SQL" di SQLite Manager per creare il database per il metodo esterno. (NOTA: si noti il ​​commento sulla tabella richiesta da 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"; 

Ecco un esempio di file update_database.sql. Deve essere inserito nella cartella delle risorse del progetto per il metodo interno o copiato in "Esegui SQL" di SQLite Manager per creare il database per il metodo esterno. (NOTA: si noti che tutti e tre i tipi di commenti SQL verranno ignorati dal parser sql incluso in questo esempio.)

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

Ecco una voce da aggiungere al file /res/values/strings.xml per il numero di versione del database.

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

Ecco un'attività che accede al database e quindi lo utilizza. ( Nota: potresti voler eseguire il codice del database in un thread separato se utilizza molte risorse. )

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

    }
}

Ecco la classe helper del database in cui il database viene creato o aggiornato, se necessario. (NOTA: Android richiede la creazione di una classe che estende SQLiteOpenHelper per funzionare con un database 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.
     */

}

Ecco la classe FileHelper che contiene metodi per il flusso di byte che copiano i file e analizzano i file 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(";");
    }

}

ho usato il codice sopra per aggiornare il mio db "upgrade_database.sql contiene l'istruzione insert. alcuni dei valori hanno un punto e virgola come inserire in valori table_a ('ss', 'ddd', 'aaaa; aaa');" quando eseguo il ho notato sopra menzione inserire non ottenere esecute a causa del punto e virgola nei valori di eventuali id ​​come risolverlo.
Sam,

5
C'è una terza opzione: copiare il db dal web. L'ho fatto e va abbastanza velocemente per un 4 megabyte. Risolve anche il problema con 2.3, per il quale la prima soluzione (copia db) non funziona.
Jack BeNimble,

2
Danny And Austyn - La tua soluzione è stata perfetta. Ho avuto problemi con la mia soluzione prodotta in casa e mi sono imbattuto nella tua. Ha davvero colpito il punto. Grazie per il tempo dedicato a fornirlo.
George Baker,

4
Preferisco di gran lunga questa risposta contro la prima votata e accettata. Ha tutte le informazioni in un unico posto (non vedo le parti di questo link) e menziona alcune specifiche di Android che non avevo idea (come CREATE TABLE "android_metadata"). Anche esempi sono scritti in grande dettaglio, il che è un vantaggio. È quasi una soluzione copia incolla che non è sempre buona, ma le spiegazioni tra il codice sono fantastiche.
Igor Čordaš

Sto usando lo stesso metodo ma sto affrontando un problema. Come possiamo copiare tutti i dati esistenti dal vecchio al nuovo file db in modo più semplice.
Pankaj,

130

La SQLiteAssetHelperlibreria rende questo compito davvero semplice.

È facile aggiungerlo come dipendenza graduale (ma è disponibile anche un vaso per Ant / Eclipse) e insieme alla documentazione è disponibile all'indirizzo:
https://github.com/jgilfelt/android-sqlite-asset-helper

Nota: questo progetto non è più gestito come indicato sopra sopra il link Github.

Come spiegato nella documentazione:

  1. Aggiungi la dipendenza al file di build gradle del tuo modulo:

    dependencies {
        compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
    }
  2. Copia il database nella directory delle risorse, in una sottodirectory chiamata assets/databases. Per esempio:
    assets/databases/my_database.db

    (Facoltativamente, è possibile comprimere il database in un file zip come assets/databases/my_database.zip. Questo non è necessario, poiché l'APK è già compresso nel suo insieme.)

  3. Crea una classe, ad esempio:

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

Il download di android-sqlite-asset-helper.jar richiede quali credenziali?
Pr38y,

1
Se si utilizza Gradle, è sufficiente aggiungere la dipendenza.
Suragch,

Come si ottengono i dati dal DB?
Machado,

È ancora più semplice con Android Studio e Gradle. Controlla il link!
Bendaf,

5
Nota che questa libreria è stata abbandonata, con l'ultimo aggiornamento 4 anni fa.
riduzione dell'attività del

13

La mia soluzione non utilizza alcuna libreria di terze parti né ti obbliga a chiamare metodi personalizzati su una SQLiteOpenHelpersottoclasse per inizializzare il database al momento della creazione. Si occupa anche degli aggiornamenti del database. Tutto ciò che deve essere fatto è sottoclassare SQLiteOpenHelper.

Prerequisiti:

  1. Il database che desideri spedire con l'app. Dovrebbe contenere una tabella 1x1 denominata android_metadatacon un attributo localecon il valore en_USin aggiunta alle tabelle univoche per la tua app.

Sottoclasse SQLiteOpenHelper:

  1. Sottoclasse SQLiteOpenHelper.
  2. Creare un privatemetodo all'interno della SQLiteOpenHelpersottoclasse. Questo metodo contiene la logica per copiare il contenuto del database dal file del database nella cartella 'assets' nel database creato nel contesto del pacchetto dell'applicazione.
  3. Override onCreate, onUpgrade e onOpen metodi di SQLiteOpenHelper.

È stato detto abbastanza. Ecco la SQLiteOpenHelpersottoclasse:

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

Infine, per ottenere una connessione al database, basta chiamare getReadableDatabase()o getWritableDatabase()nella SQLiteOpenHelpersottoclasse e si occuperà della creazione di un db, copiando il contenuto del db dal file specificato nella cartella 'assets', se il database non esiste.

In breve, è possibile utilizzare la SQLiteOpenHelpersottoclasse per accedere al db fornito nella cartella delle risorse proprio come si farebbe per un database inizializzato utilizzando le query SQL nel onCreate()metodo.


2
Questa è la soluzione più elegante, utilizzando le API Androids standard senza la necessità di librerie esterne. Come nota, non ho incluso la tabella android_metadata e funziona, le nuove versioni di Android potrebbero aggiungerlo automaticamente.
goetzc,

12

Spedisci l'app con un file di database, in Android Studio 3.0

Spedire l'app con un file di database è una buona idea per me. Il vantaggio è che non è necessario eseguire un'inizializzazione complessa, che a volte costa molto tempo, se il set di dati è enorme.

Passaggio 1: preparare il file di database

Tieni pronto il tuo file di database. Può essere un file .db o un file .sqlite. Se usi un file .sqlite, tutto ciò che devi fare è cambiare i nomi delle estensioni di file. I passaggi sono gli stessi.

In questo esempio, ho preparato un file chiamato testDB.db. Ha una tabella e alcuni dati di esempio in questo modo inserisci qui la descrizione dell'immagine

Passaggio 2: importa il file nel tuo progetto

Crea la cartella delle risorse se non ne hai avuta una. Quindi copia e incolla il file del database in questa cartella

inserisci qui la descrizione dell'immagine

Passaggio 3: copiare il file nella cartella dei dati dell'app

È necessario copiare il file di database nella cartella dei dati dell'app per poter interagire ulteriormente con esso. Questa è un'azione una tantum (inizializzazione) per copiare il file di database. Se si chiama questo codice più volte, il file di database nella cartella dei dati verrà sovrascritto da quello nella cartella delle risorse. Questo processo di sovrascrittura è utile quando si desidera aggiornare il database in futuro durante l'aggiornamento dell'app.

Durante l'aggiornamento dell'app, questo file di database non verrà modificato nella cartella dei dati dell'app. Solo la disinstallazione lo eliminerà.

Il file del database deve essere copiato nella /databasescartella. Apri Esplora file dispositivo. Inserisci data/data/<YourAppName>/posizione. Questa è la cartella dati predefinita dell'app menzionata sopra. E per impostazione predefinita, il file di database verrà inserito in un'altra cartella denominata database in questa directory

inserisci qui la descrizione dell'immagine

Ora, il processo di copia dei file è simile a quello che sta facendo Java. Utilizzare il seguente codice per eseguire il copia incolla. Questo è il codice di inizio. Può anche essere utilizzato per aggiornare (sovrascrivendo) il file di database in futuro.

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

Quindi aggiornare la cartella per verificare il processo di copia

inserisci qui la descrizione dell'immagine

Passaggio 4: creare l'helper aperto del database

Creare una sottoclasse per SQLiteOpenHelper, con connect, close, path, ecc. L'ho chiamatoDatabaseOpenHelper

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

Passaggio 5: creare una classe di livello superiore per interagire con il database

Questa sarà la classe che legge e scrive il tuo file di database. Inoltre c'è una query di esempio per stampare il valore nel database.

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

Passaggio 6: test in esecuzione

Testare il codice eseguendo le seguenti righe di codice.

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

Premi il pulsante Esegui e fai il tifo!

inserisci qui la descrizione dell'immagine


1
quando deve essere eseguita l'inizializzazione? Qual è la strategia che suggerisci?
Daniele B,

8

Nel novembre 2017 Google ha rilasciato la biblioteca di persistenza della stanza .

Dalla documentazione:

La libreria di persistenza Room offre un livello di astrazione su SQ strong text Lite per consentire un accesso fluido al database sfruttando la piena potenza di SQLite .

La libreria ti aiuta a creare una cache dei dati della tua app su un dispositivo che esegue la tua app. Questa cache, che funge da unica fonte di verità per l'app, consente agli utenti di visualizzare una copia coerente delle informazioni chiave all'interno dell'app, indipendentemente dal fatto che gli utenti dispongano di una connessione Internet.

Il database Room ha un callback quando il database viene creato o aperto per la prima volta. È possibile utilizzare il call call di creazione per popolare il database.

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

Codice da questo post sul blog .


Grazie, ha funzionato per me. Esempio Java qui
Jerry Sha,

1
Se desideri spedire un APK con un SQLite già esistente, puoi aggiungerlo alla cartella delle risorse e utilizzare questo pacchetto github.com/humazed/RoomAsset per eseguire una migrazione che caricherà i dati del file SQLite in quello nuovo. In questo modo, è possibile salvare il popolamento dei dati con un DB esistente.
xarlymg89,

6

Da quello che ho visto dovresti spedire un database che ha già la configurazione delle tabelle e dei dati. Tuttavia, se lo desideri (e in base al tipo di applicazione che possiedi) puoi consentire "opzione di aggiornamento del database". Quindi quello che fai è scaricare l'ultima versione di sqlite, ottenere le ultime istruzioni Inserisci / Crea di un file di testo ospitato online, eseguire le istruzioni ed eseguire un trasferimento di dati dal vecchio db a quello nuovo.


6
> Da quello che ho visto dovresti spedire un database che ha già la configurazione delle tabelle e dei dati. Sì, ma come si fa?
Rory,

5

Finalmente l'ho fatto !! Ho usato questo link help Usando il tuo database SQLite nelle applicazioni Android , ma ho dovuto cambiarlo un po '.

  1. Se hai molti pacchetti, dovresti inserire qui il nome del pacchetto principale:

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

  2. Ho cambiato il metodo che copia il database dalla cartella locale alla cartella dell'emulatore! Aveva qualche problema quando quella cartella non esisteva. Quindi, prima di tutto, dovrebbe controllare il percorso e se non è lì, dovrebbe creare la cartella.

  3. Nel codice precedente, il copyDatabasemetodo non era mai stato chiamato quando il database non esisteva e il checkDataBasemetodo causava un'eccezione. quindi ho cambiato un po 'il codice.

  4. Se il tuo database non ha un'estensione di file, non utilizzare il nome del file con uno.

funziona bene per me, spero che possa essere utile anche per te

    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.

}

per favore fatemi sapere come aggiornare db se voglio sostituire il vecchio database con uno nuovo come posso eliminare il vecchio db
Erum

Non ho bisogno di farlo fino ad ora, ma se è stata installata una nuova app, sostituisce anche il nuovo db
afsane

come eliminare il vecchio database perché sto aggiungendo un nuovo db nella cartella delle risorse, quindi come cancellerò il vecchio db dalla cartella specificata altrimenti porterò il contenuto del vecchio db
Erum

Spero che questo sarebbe utile stackoverflow.com/questions/9109438/...
Afsane

Perfetto, grazie! Solo un commento, il lancio dell'eccezione durante il controllo del database provoca la chiusura dell'app, poiché il DB non sarà presente all'inizio e il metodo non continua dopo che viene generata l'eccezione. Ho semplicemente commentato il nuovo errore di lancio ("dose database non esiste"); e ora tutto funziona perfettamente.
Grinner,

4

Attualmente non c'è modo di precreare un database SQLite da spedire con il tuo apk. Il meglio che puoi fare è salvare l'SQL appropriato come risorsa ed eseguirli dalla tua applicazione. Sì, questo porta alla duplicazione dei dati (esistono le stesse informazioni come resrouce e come database) ma al momento non c'è altro modo. L'unico fattore attenuante è che il file apk è compresso. La mia esperienza è di 908 KB compressi a meno di 268 KB.

Il thread sotto ha la migliore discussione / soluzione che ho trovato con un buon codice di esempio.

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

Ho archiviato la mia istruzione CREATE come risorsa stringa da leggere con Context.getString () e l'ho eseguita con SQLiteDatabse.execSQL ().

Ho archiviato i dati per i miei inserti in res / raw / inserts.sql (ho creato il file sql, 7000+ righe). Usando la tecnica dal link sopra ho inserito un ciclo, ho letto il file riga per riga e ho ridotto i dati su "INSERT INTO tbl VALUE" e ho fatto un altro SQLiteDatabase.execSQL (). Non ha senso salvare 7000 "INSERT INTO tbl VALUE" quando possono essere semplicemente concatenati.

Ci vogliono circa venti secondi sull'emulatore, non so quanto tempo impiegherebbe un telefono reale, ma succede solo una volta, quando l'utente avvia per la prima volta l'applicazione.


3
Che ne dici di estrarre lo script SQL dal web alla prima esecuzione? In questo modo non è necessario duplicare i dati.
Tamas Czinege,

1
Sì, ma il dispositivo dovrà essere connesso a Internet. Questo è un grave inconveniente in alcune app.
Dzhuneyt,

Non eseguire oltre 7000 inserti, eseguire inserimenti batch di circa 100 in questo modo INSERT INTO table VALUES(...) VALUES(...) VALUES(...) ...(1 riga di inserimento deve avere 100 VALORI). Sarà molto più efficiente e ridurrà il tempo di avvio da 20 secondi a 2 o 3 secondi.
Mohit Atray, l'

4

Spedire il database all'interno dell'apk e poi copiarlo /data/data/...raddoppierà le dimensioni del database (1 in apk, 1 in data/data/...) e aumenterà la dimensione dell'apk (ovviamente). Quindi il tuo database non dovrebbe essere troppo grande.


2
Aumenta in qualche modo la dimensione dell'apk ma non la raddoppia. Quando è negli asset è compresso e quindi è molto più piccolo. Dopo averlo copiato nella cartella del database, non viene compresso.
Suragch,

3

Android fornisce già un approccio consapevole della versione della gestione del database. Questo approccio è stato sfruttato nel framework BARACUS per le applicazioni Android.

Ti consente di gestire il database lungo l'intero ciclo di vita della versione di un'app, essendo in grado di aggiornare il database sqlite da qualsiasi versione precedente a quella corrente.

Inoltre, consente di eseguire hot-backup e hot-recovery di SQLite.

Non sono sicuro al 100%, ma un ripristino a caldo per un dispositivo specifico potrebbe consentire di spedire un database preparato nella tua app. Ma non sono sicuro del formato binario del database che potrebbe essere specifico per determinati dispositivi, fornitori o generazioni di dispositivi.

Dato che il materiale è Apache License 2, sentiti libero di riutilizzare qualsiasi parte del codice, che puoi trovare su github

MODIFICARE :

Se si desidera solo spedire dati, è possibile prendere in considerazione la creazione di istanze e la conservazione di POJO al primo avvio delle applicazioni. BARACUS ha ottenuto un supporto integrato in questo (archivio di valori chiave incorporato per informazioni sulla configurazione, ad es. "APP_FIRST_RUN" più un hook di bootstrap post-contesto per eseguire operazioni post-avvio sul contesto). Ciò ti consente di avere dati strettamente associati spediti con la tua app; nella maggior parte dei casi questo si adattava ai miei casi d'uso.


3

Se i dati richiesti non sono troppo grandi (i limiti che non conosco dipendono da molte cose), è possibile anche scaricare i dati (in XML, JSON, qualunque cosa) da un sito Web / webapp. Dopo la ricezione, eseguire le istruzioni SQL utilizzando i dati ricevuti creando le tabelle e inserendo i dati.

Se l'app per dispositivi mobili contiene molti dati, in seguito potrebbe essere più semplice aggiornare i dati nelle app installate con dati o modifiche più precisi.


3

Ho modificato la classe e le risposte alla domanda e ho scritto una classe che consente l'aggiornamento del database tramite 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;
    }
}

Usando una classe.

Nella classe di attività, dichiarare le variabili.

private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;

Nel metodo onCreate, scrivi il seguente codice.

mDBHelper = new DatabaseHelper(this);

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

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

Se si aggiunge un file di database alla cartella res / raw, utilizzare la seguente modifica della 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


2

Ho scritto una biblioteca per semplificare questo processo.

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

Creerà una base di dati dal assets/databases/myDb.dbfile. Inoltre otterrai tutte quelle funzionalità:

  • Carica database da file
  • Accesso sincronizzato al database
  • Utilizzando sqlite-android su richiesta, distribuzione specifica Android delle ultime versioni di SQLite.

Clonalo da Github .


2

Sto usando ORMLite e sotto il codice ha funzionato per me

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

    }
}

Nota: il codice estrae il file di database da un file zip nelle risorse

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.