Enviar una aplicación con una base de datos

959

Si su aplicación requiere una base de datos y viene con datos integrados, ¿cuál es la mejor manera de enviar esa aplicación? Debería:

  1. Precrear la base de datos SQLite e incluirla en el .apk?

  2. ¿Incluya los comandos SQL con la aplicación y haga que cree la base de datos e inserte los datos en el primer uso?

Los inconvenientes que veo son:

  1. Los posibles desajustes de la versión de SQLite pueden causar problemas y actualmente no sé a dónde debe ir la base de datos y cómo acceder a ella.

  2. Puede tomar mucho tiempo crear y llenar la base de datos en el dispositivo.

¿Alguna sugerencia? Se agradecerán los indicadores de la documentación sobre cualquier problema.

Heikki Toivonen
fuente
66
use SQLiteAssetHelper
Richard Le Mesurier

Respuestas:

199

Hay dos opciones para crear y actualizar bases de datos.

Una es crear una base de datos externamente, luego colocarla en la carpeta de activos del proyecto y luego copiar toda la base de datos desde allí. Esto es mucho más rápido si la base de datos tiene muchas tablas y otros componentes. Las actualizaciones se activan cambiando el número de versión de la base de datos en el archivo res / values ​​/ strings.xml. Las actualizaciones se realizarían creando una nueva base de datos externamente, reemplazando la base de datos anterior en la carpeta de activos con la nueva base de datos, guardando la base de datos anterior en el almacenamiento interno con otro nombre, copiando la nueva base de datos de la carpeta de activos en el almacenamiento interno, transfiriendo todo de los datos de la base de datos anterior (que se renombró anteriormente) a la nueva base de datos y finalmente se elimina la base de datos anterior. Puede crear una base de datos originalmente utilizando elComplemento SQLite Manager FireFox para ejecutar sus declaraciones SQL de creación.

La otra opción es crear una base de datos internamente a partir de un archivo sql. Esto no es tan rápido, pero el retraso probablemente pasará desapercibido para los usuarios si la base de datos tiene solo unas pocas tablas. Las actualizaciones se activan cambiando el número de versión de la base de datos en el archivo res / values ​​/ strings.xml. Las actualizaciones se realizarían procesando un archivo sql de actualización. Los datos en la base de datos permanecerán sin cambios, excepto cuando se elimine su contenedor, por ejemplo, soltar una tabla.

El siguiente ejemplo muestra cómo usar cualquiera de los métodos.

Aquí hay un archivo de muestra create_database.sql. Debe colocarse en la carpeta de activos del proyecto para el método interno o copiarse en el "Ejecutar SQL 'de SQLite Manager para crear la base de datos para el método externo. (NOTA: Observe el comentario sobre la tabla requerida por 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"; 

Aquí hay un archivo de ejemplo update_database.sql. Debe colocarse en la carpeta de activos del proyecto para el método interno o copiarse en el "Ejecutar SQL" de SQLite Manager para crear la base de datos para el método externo. (NOTA: Tenga en cuenta que los tres tipos de comentarios SQL serán ignorados por el analizador sql que se incluye en este ejemplo).

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

Aquí hay una entrada para agregar al archivo /res/values/strings.xml para el número de versión de la base de datos.

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

Aquí hay una actividad que accede a la base de datos y luego la usa. ( Nota: es posible que desee ejecutar el código de la base de datos en un hilo separado si usa muchos recursos ) .

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

    }
}

Aquí está la clase auxiliar de la base de datos donde la base de datos se crea o actualiza si es necesario. (NOTA: Android requiere que cree una clase que amplíe SQLiteOpenHelper para poder trabajar con una base de datos 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.
     */

}

Aquí está la clase FileHelper que contiene métodos para copiar archivos de flujo de bytes y analizar archivos sql.

package android.example;

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

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

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

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

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

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

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

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

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

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

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

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

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

        }

        sqlFile.close();

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

}
Danny Remington - OMS
fuente
utilicé el código anterior para actualizar mi db "upgrade_database.sql contiene una declaración de inserción. algunos de los valores tienen punto y coma como insertar en valores table_a ('ss', 'ddd', 'aaaa; aaa');" cuando ejecuto el Noté que la inserción mencionada anteriormente no se ejecuta debido al punto y coma en los valores de cualquier idea de cómo solucionar esto.
Sam
55
Hay una tercera opción: copiar el db de la web. He hecho esto y va bastante rápido para un 4 meg db. También resuelve el problema con 2.3, para el cual la primera solución (copy db) no funciona.
Jack BeNimble
2
Danny y Austyn: su solución fue perfecta. Estaba teniendo problemas con mi solución casera y tropecé con la suya. Realmente dio en el clavo. Gracias por tomarse el tiempo para proporcionarlo.
George Baker,
44
Prefiero esta respuesta contra la más votada y aceptada. Tiene toda la información en un solo lugar (no ve las partes de este enlace) y menciona algunos detalles de Android que no tenía idea de que existían (como CREATE TABLE "android_metadata"). También se escriben ejemplos con gran detalle, lo cual es una ventaja. Es casi una solución de copiar y pegar que no siempre es buena, pero las explicaciones entre el código son excelentes.
Igor Čordaš
Estoy usando el mismo método pero me enfrento a un problema: cómo podemos copiar todos los datos existentes del archivo db antiguo al nuevo de una manera más fácil.
Pankaj
130

La SQLiteAssetHelperbiblioteca hace que esta tarea sea realmente simple.

Es fácil agregarlo como una dependencia de gradle (pero también está disponible un Jar para Ant / Eclipse), y junto con la documentación se puede encontrar en:
https://github.com/jgilfelt/android-sqlite-asset-helper

Nota: Este proyecto ya no se mantiene como se indica en el enlace de Github anterior.

Como se explica en la documentación:

  1. Agregue la dependencia al archivo de compilación gradle de su módulo:

    dependencies {
        compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
    }
  2. Copie la base de datos en el directorio de activos, en un subdirectorio llamado assets/databases. Por ejemplo:
    assets/databases/my_database.db

    (Opcionalmente, puede comprimir la base de datos en un archivo zip como assets/databases/my_database.zip. Esto no es necesario, ya que el APK ya está comprimido como un todo).

  3. Crea una clase, por ejemplo:

    public class MyDatabase extends SQLiteAssetHelper {
    
        private static final String DATABASE_NAME = "my_database.db";
        private static final int DATABASE_VERSION = 1;
    
        public MyDatabase(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
    }
DavidEG
fuente
¿Qué credencial requiere la descarga de android-sqlite-asset-helper.jar?
Pr38y
1
Si está utilizando gradle, simplemente agrega la dependencia.
Suragch
¿Cómo se obtienen los datos de la base de datos?
Machado
Es aún más fácil con Android Studio y gradle. ¡Mira el enlace!
bendaf
55
Tenga en cuenta que esta biblioteca está abandonada, con la última actualización hace 4 años.
reducción de actividad
13

Mi solución no utiliza ninguna biblioteca de terceros ni le obliga a llamar a métodos personalizados en la SQLiteOpenHelpersubclase para inicializar la base de datos en la creación. También se encarga de las actualizaciones de la base de datos. Todo lo que hay que hacer es subclasificar SQLiteOpenHelper.

Requisito previo:

  1. La base de datos que desea enviar con la aplicación. Debe contener una tabla 1x1 nombrada android_metadatacon un atributo que localetenga el valor en_USademás de las tablas exclusivas de su aplicación.

Subclases SQLiteOpenHelper:

  1. Subclase SQLiteOpenHelper.
  2. Crea un privatemétodo dentro de la SQLiteOpenHelpersubclase. Este método contiene la lógica para copiar el contenido de la base de datos del archivo de la base de datos en la carpeta 'activos' a la base de datos creada en el contexto del paquete de la aplicación.
  3. Anular onCreate,onUpgrade y onOpen métodos de SQLiteOpenHelper.

Basta de charla. Aquí va la SQLiteOpenHelpersubclase:

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

Finalmente, para obtener una conexión de base de datos, solo llame getReadableDatabase() o getWritableDatabase()en la SQLiteOpenHelpersubclase y se encargará de crear una base de datos, copiando el contenido de la base de datos del archivo especificado en la carpeta 'assets', si la base de datos no existe.

En resumen, puede usar la SQLiteOpenHelpersubclase para acceder a la base de datos incluida en la carpeta de activos tal como lo usaría para una base de datos que se inicializa mediante consultas SQL en el onCreate()método.

Vaishak Nair
fuente
2
Esta es la solución más elegante, utilizando API de Android estándar sin la necesidad de bibliotecas externas. Como nota, no incluí la tabla android_metadata y funciona, las versiones más recientes de Android podrían agregarla automáticamente.
goetzc
12

Envío de la aplicación con un archivo de base de datos, en Android Studio 3.0

Enviar la aplicación con un archivo de base de datos es una buena idea para mí. La ventaja es que no necesita hacer una inicialización compleja, que a veces cuesta mucho tiempo, si su conjunto de datos es enorme.

Paso 1: preparar el archivo de base de datos

Tenga listo su archivo de base de datos. Puede ser un archivo .db o un archivo .sqlite. Si usa un archivo .sqlite, todo lo que necesita hacer es cambiar los nombres de las extensiones de archivo. Los pasos son los mismos.

En este ejemplo, preparé un archivo llamado testDB.db. Tiene una tabla y algunos datos de muestra como esta ingrese la descripción de la imagen aquí

Paso 2: importa el archivo a tu proyecto

Cree la carpeta de activos si no ha tenido una. Luego copie y pegue el archivo de la base de datos en esta carpeta

ingrese la descripción de la imagen aquí

Paso 3: copie el archivo en la carpeta de datos de la aplicación

Debe copiar el archivo de la base de datos a la carpeta de datos de la aplicación para poder interactuar más con ella. Esta es una acción única (inicialización) para copiar el archivo de la base de datos. Si llama a este código varias veces, el archivo de la base de datos en la carpeta de datos será sobrescrito por el de la carpeta de activos. Este proceso de sobrescritura es útil cuando desea actualizar la base de datos en el futuro durante la actualización de la aplicación.

Tenga en cuenta que durante la actualización de la aplicación, este archivo de base de datos no se cambiará en la carpeta de datos de la aplicación. Solo la desinstalación lo eliminará.

El archivo de la base de datos debe copiarse en la /databasescarpeta. Abra el Explorador de archivos del dispositivo. Ingrese data/data/<YourAppName>/ubicación. Esta es la carpeta de datos predeterminada de la aplicación mencionada anteriormente. Y de manera predeterminada, el archivo de la base de datos se colocará en otra carpeta llamada bases de datos en este directorio

ingrese la descripción de la imagen aquí

Ahora, el proceso de copia del archivo es muy parecido al que está haciendo Java. Use el siguiente código para copiar y pegar. Este es el código de iniciación. También se puede usar para actualizar (sobrescribiendo) el archivo de la base de datos en el 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
}

Luego actualice la carpeta para verificar el proceso de copia

ingrese la descripción de la imagen aquí

Paso 4: Crear asistente abierto de base de datos

Crea una subclase para SQLiteOpenHelper, con connect, close, path, etc. Lo llaméDatabaseOpenHelper

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

Paso 5: crea una clase de nivel superior para interactuar con la base de datos

Esta será la clase que leerá y escribirá su archivo de base de datos. También hay una consulta de muestra para imprimir el valor en la base de datos.

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

Paso 6: prueba de funcionamiento

Pruebe el código ejecutando las siguientes líneas de códigos.

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

¡Presiona el botón de correr y anima!

ingrese la descripción de la imagen aquí

Colmillo
fuente
1
¿Cuándo debe hacerse la inicialización? ¿Cuál es la estrategia que sugieres?
Daniele B
8

En noviembre de 2017, Google lanzó la Biblioteca de persistencia de habitaciones .

De la documentación:

La biblioteca de persistencia de la sala proporciona una capa de abstracción sobre texto sólido SQ Lite para permitir un acceso fluido a la base de datos mientras aprovecha toda la potencia de SQLite .

La biblioteca lo ayuda a crear un caché de los datos de su aplicación en un dispositivo que ejecuta su aplicación. Este caché, que sirve como la única fuente de verdad de su aplicación, permite a los usuarios ver una copia consistente de la información clave dentro de su aplicación, independientemente de si los usuarios tienen una conexión a Internet.

La base de datos de la sala tiene una devolución de llamada cuando la base de datos se crea o abre por primera vez. Puede usar la devolución de llamada de creación para completar su base de datos.

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

Código de esta publicación de blog .

LordRaydenMK
fuente
Gracias, esto funcionó para mí. Ejemplo de Java aquí
Jerry Sha
1
Si desea enviar un APK con un SQLite ya existente, puede agregarlo a la carpeta de activos y usar este paquete github.com/humazed/RoomAsset para realizar una migración que cargará los datos del archivo SQLite en el nuevo. De esta manera, puede guardar el llenado de datos con una base de datos existente.
xarlymg89
6

Por lo que he visto, deberías enviar una base de datos que ya tenga la configuración de tablas y los datos. Sin embargo, si lo desea (y dependiendo del tipo de aplicación que tenga) puede permitir la "opción de actualización de la base de datos". Luego, lo que debe hacer es descargar la última versión de sqlite, obtener las últimas declaraciones Insertar / Crear de un archivo de texto alojado en línea, ejecutar las declaraciones y hacer una transferencia de datos desde el antiguo db al nuevo.

Masfenix
fuente
66
> Por lo que he visto, deberías enviar una base de datos que ya tenga la configuración de tablas y los datos. Sí, pero ¿cómo haces esto?
Rory
5

¡¡Finalmente lo hice!! He usado este enlace de ayuda Usando su propia base de datos SQLite en aplicaciones de Android , pero tuve que cambiarlo un poco.

  1. Si tiene muchos paquetes, debe poner el nombre del paquete maestro aquí:

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

  2. ¡Cambié el método que copia la base de datos de la carpeta local a la carpeta del emulador! Tuvo algún problema cuando esa carpeta no existía. Entonces, antes que nada, debe verificar la ruta y, si no está allí, debe crear la carpeta.

  3. En el código anterior, el copyDatabasemétodo nunca se llamaba cuando la base de datos no existía y el checkDataBasemétodo causaba una excepción. Así que cambié un poco el código.

  4. Si su base de datos no tiene una extensión de archivo, no use el nombre del archivo con una.

funciona bien para mí, espero que sea útil para ti también

    package farhangsarasIntroduction;


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

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

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

import android.util.Log;


    public class DataBaseHelper extends SQLiteOpenHelper{

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

    private static String DB_NAME = "farhangsaraDb";

    private SQLiteDatabase myDataBase;

    private final Context myContext;

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

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

    }   

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

        boolean dbExist;
        try {

             dbExist = checkDataBase();


        } catch (SQLiteException e) {

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

        }

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

            try {

                copyDataBase();


            } catch (IOException e) {

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

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


    }

    }

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

    SQLiteDatabase checkDB = null;

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

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

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

    }

    if(checkDB != null){

    checkDB.close();

    }

    return checkDB != null ? true : false;
    }

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



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

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

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

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

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



    }



    @Override
    public synchronized void close() {

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

        super.close();

    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }



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

    }

     you to create adapters for your views.

}
afsane
fuente
puede u por favor hágamelo saber cómo actualizar db si quiero reemplazar la base de datos antigua por una nueva, ¿cómo puedo eliminar edad db
Erum
No tengo que hacer esto hasta ahora, pero si se ha instalado una nueva aplicación, la nueva base de datos también reemplaza
afsane
cómo eliminar la base de datos anterior porque estoy agregando nueva base de datos en la carpeta de activos, entonces ¿cómo eliminaré la antigua base de datos de la carpeta especificada? De lo contrario, traerá el contenido de la antigua base de datos
Erum
Espero que esto sea útil stackoverflow.com/questions/9109438/…
afsane
¡Perfecto, gracias! Solo un comentario, lanzar la excepción al verificar la base de datos hace que la aplicación se cierre, ya que la base de datos no estará allí al principio y el método no continuará después de que se lance la excepción. Simplemente comenté el error de lanzamiento nuevo ("la dosis de la base de datos no existe"); y ahora todo funciona a la perfección.
Grinner
4

Actualmente no hay forma de crear una base de datos SQLite para enviar con su apk. Lo mejor que puede hacer es guardar el SQL apropiado como un recurso y ejecutarlo desde su aplicación. Sí, esto conduce a la duplicación de datos (la misma información existe como un recurso y como una base de datos) pero no hay otra manera en este momento. El único factor atenuante es que el archivo apk está comprimido. Mi experiencia es de 908 KB a menos de 268 KB.

El siguiente hilo tiene la mejor discusión / solución que he encontrado con un buen código de muestra.

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

Almacené mi declaración CREATE como un recurso de cadena para leer con Context.getString () y la ejecuté con SQLiteDatabse.execSQL ().

Almacené los datos para mis inserciones en res / raw / inserts.sql (creé el archivo sql, más de 7000 líneas). Utilizando la técnica del enlace anterior, ingresé un bucle, leí el archivo línea por línea y concatene los datos en "INSERT INTO tBL VALUE" e hice otro SQLiteDatabase.execSQL (). No tiene sentido guardar 7000 "INSERTAR EN EL VALOR tbl" cuando simplemente se pueden concatenar.

Se tarda unos veinte segundos en el emulador, no sé cuánto tiempo llevaría en un teléfono real, pero solo ocurre una vez, cuando el usuario inicia la aplicación por primera vez.

Será
fuente
3
¿Qué tal sacar el script SQL de la web en la primera ejecución? De esta manera no hay necesidad de duplicar datos.
Tamas Czinege
1
Sí, pero el dispositivo deberá estar conectado a Internet. Eso es un serio inconveniente en algunas aplicaciones.
Dzhuneyt
No haga más de 7000 inserciones, haga inserciones por lotes de aproximadamente 100 de esta manera INSERT INTO table VALUES(...) VALUES(...) VALUES(...) ...(1 línea de inserción debe tener 100 VALORES). Será mucho más eficiente y reducirá el tiempo de inicio de 20 segundos a 2 o 3 segundos.
Mohit Atray
4

El envío de la base de datos dentro del apk y luego copiarlo /data/data/...duplicará el tamaño de la base de datos (1 en apk, 1 in data/data/...) y aumentará el tamaño del apk (por supuesto). Entonces su base de datos no debería ser demasiado grande.

Hiep
fuente
2
Aumenta un poco el tamaño del apk, pero no lo duplica. Cuando está en activos, está comprimido y, por lo tanto, es mucho más pequeño. Después de copiarlo en la carpeta de la base de datos, se descomprime.
Suragch
3

Android ya ofrece un enfoque de gestión de bases de datos basado en versiones. Este enfoque se ha aprovechado en el marco BARACUS para aplicaciones de Android.

Le permite administrar la base de datos a lo largo del ciclo de vida completo de la versión de una aplicación, pudiendo actualizar la base de datos sqlite de cualquier versión anterior a la actual.

Además, le permite ejecutar copias de seguridad en caliente y recuperación en caliente de SQLite.

No estoy 100% seguro, pero una recuperación en caliente para un determinado dispositivo puede permitirle enviar una base de datos preparada en su aplicación. Pero no estoy seguro sobre el formato binario de la base de datos que podría ser específico para ciertos dispositivos, proveedores o generaciones de dispositivos.

Como el material es Apache License 2, siéntase libre de reutilizar cualquier parte del código, que se puede encontrar en github

EDITAR:

Si solo desea enviar datos, puede considerar crear instancias y persistir POJO en el inicio de las aplicaciones. BARACUS obtuvo un soporte integrado para esto (Almacén de valores de clave incorporado para información de configuración, por ejemplo, "APP_FIRST_RUN" más un gancho de arranque después del contexto para ejecutar operaciones posteriores al lanzamiento en el contexto). Esto le permite tener datos acoplados estrechamente enviados con su aplicación; en la mayoría de los casos esto se ajustaba a mis casos de uso.

Gorefest
fuente
3

Si los datos requeridos no son demasiado grandes (los límites que no sé, dependerían de muchas cosas), también puede descargar los datos (en XML, JSON, lo que sea) de un sitio web / aplicación web. Después de recibir, ejecute las instrucciones SQL utilizando los datos recibidos creando sus tablas e insertando los datos.

Si su aplicación móvil contiene muchos datos, podría ser más fácil actualizarlos en las aplicaciones instaladas con datos o cambios más precisos.

Jaco
fuente
3

Modifiqué la clase y las respuestas a la pregunta y escribí una clase que permite actualizar la base de datos a través de 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 clase.

En la clase de actividad, declare variables.

private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;

En el método onCreate, escriba el siguiente código.

mDBHelper = new DatabaseHelper(this);

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

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

Si agrega un archivo de base de datos a la carpeta res / raw, use la siguiente modificación de la clase.

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

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

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

        copyDataBase();

        this.getReadableDatabase();
    }

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

            copyDataBase();

            mNeedUpdate = false;
        }
    }

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

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

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

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

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

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

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

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

Harrix
fuente
2

Escribí una biblioteca para simplificar este proceso.

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

Creará una base de datos a partir del assets/databases/myDb.dbarchivo. Además obtendrá todas esas funcionalidades:

  • Cargar base de datos desde archivo
  • Acceso sincronizado a la base de datos.
  • Usando sqlite-android por requery, distribución específica de Android de las últimas versiones de SQLite.

Clonarlo desde github .

Ilya Gazman
fuente
2

Estoy usando ORMLite y el siguiente código funcionó para mí

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

    }
}

Tenga en cuenta que el código extrae el archivo de la base de datos de un archivo zip en los activos

Homayoun Behzadian
fuente