Enviar um aplicativo com um banco de dados

959

Se seu aplicativo requer um banco de dados e ele vem com dados incorporados, qual é a melhor maneira de enviá-lo? Eu devo:

  1. Pré-crie o banco de dados SQLite e inclua-o no .apk?

  2. Inclua os comandos SQL no aplicativo e faça com que ele crie o banco de dados e insira os dados no primeiro uso?

As desvantagens que vejo são:

  1. Possíveis incompatibilidades de versão do SQLite podem causar problemas e, atualmente, não sei para onde o banco de dados deve ir e como acessá-lo.

  2. Pode levar muito tempo para criar e preencher o banco de dados no dispositivo.

Alguma sugestão? Os ponteiros da documentação sobre quaisquer problemas seriam muito apreciados.

Heikki Toivonen
fonte
6
use SQLiteAssetHelper
Richard Le Mesurier

Respostas:

199

Existem duas opções para criar e atualizar bancos de dados.

Uma é criar um banco de dados externamente, colocá-lo na pasta de ativos do projeto e copiar todo o banco de dados a partir daí. Isso é muito mais rápido se o banco de dados tiver muitas tabelas e outros componentes. As atualizações são acionadas alterando o número da versão do banco de dados no arquivo res / values ​​/ strings.xml. As atualizações seriam realizadas criando um novo banco de dados externamente, substituindo o banco de dados antigo na pasta de ativos pelo novo banco de dados, salvando o banco de dados antigo no armazenamento interno com outro nome, copiando o novo banco de dados da pasta de ativos para o armazenamento interno, transferindo todos os dos dados do banco de dados antigo (que foi renomeado anteriormente) para o novo banco de dados e, finalmente, excluindo o banco de dados antigo. Você pode criar um banco de dados originalmente usando oSQLite Manager FireFox plugin para executar suas instruções sql de criação.

A outra opção é criar um banco de dados internamente a partir de um arquivo sql. Isso não é tão rápido, mas o atraso provavelmente seria imperceptível para os usuários se o banco de dados tivesse apenas algumas tabelas. As atualizações são acionadas alterando o número da versão do banco de dados no arquivo res / values ​​/ strings.xml. As atualizações seriam realizadas através do processamento de um arquivo sql de atualização. Os dados no banco de dados permanecerão inalterados, exceto quando o contêiner for removido, por exemplo, descartando uma tabela.

O exemplo abaixo demonstra como usar qualquer método.

Aqui está um arquivo create_database.sql de amostra. Ele deve ser colocado na pasta de ativos do projeto para o método interno ou copiado para "Executar SQL 'do SQLite Manager para criar o banco de dados para o método externo. (NOTA: Observe o comentário sobre a tabela exigida pelo 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"; 

Aqui está um exemplo de arquivo update_database.sql. Ele deve ser colocado na pasta de ativos do projeto para o método interno ou copiado para "Executar SQL 'do SQLite Manager para criar o banco de dados para o método externo. (NOTA: Observe que todos os três tipos de comentários SQL serão ignorados pelo analisador sql incluído neste exemplo.)

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

Aqui está uma entrada para adicionar ao arquivo /res/values/strings.xml do número da versão do banco de dados.

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

Aqui está uma atividade que acessa o banco de dados e depois o usa. ( Nota: convém executar o código do banco de dados em um encadeamento separado se ele usar muitos 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();
            }
        }

    }
}

Aqui está a classe auxiliar do banco de dados em que o banco de dados é criado ou atualizado, se necessário. (NOTA: O Android exige que você crie uma classe que estenda o SQLiteOpenHelper para trabalhar com um banco de dados 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.
     */

}

Aqui está a classe FileHelper que contém métodos para fluxo de bytes, cópia de arquivos e análise de arquivos 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
fonte
Eu usei o código acima para atualizar meu db "upgrade_database.sql contém a instrução insert. alguns dos valores têm ponto e vírgula como inserir na tabela_a valores ('ss', 'ddd', 'aaaa; aaa');" quando executo o notei acima mencionar insert não ficando esecute por causa do semicoln nos valores qualquer ides como corrigir isso.
Sam
5
Existe uma terceira opção - copie o banco de dados da web. Eu fiz isso e vai rapidamente para um dB de 4 meg. Ele também resolve o problema com o 2.3, para o qual a primeira solução (cópia db) não funciona.
Jack BeNimble
2
Danny And Austyn - Sua solução foi perfeita. Eu estava tendo problemas com a minha solução fabricada em casa e deparei com a sua. Realmente atingiu o local. Obrigado por reservar um tempo para fornecê-lo.
George Baker
4
Eu prefiro muito esta resposta contra a primeira votada e aceita. Ele possui todas as informações em um único local (não há partes deste link) e mencionou algumas especificações do Android que eu não imaginava (como CREATE TABLE "android_metadata"). Também exemplos são escritos em grandes detalhes, o que é uma vantagem. É quase uma solução de copiar e colar, que nem sempre é boa, mas as explicações entre o código são ótimas.
Igor Čordaš
Estou usando o mesmo método, mas estou enfrentando um problema. Como podemos copiar todos os dados existentes do antigo para o novo arquivo db da maneira mais fácil.
Pankaj
130

A SQLiteAssetHelperbiblioteca torna essa tarefa realmente simples.

É fácil adicionar como dependência gradle (mas um Jar também está disponível para Ant / Eclipse) e, juntamente com a documentação, pode ser encontrado em:
https://github.com/jgilfelt/android-sqlite-asset-helper

Nota: Este projeto não é mais mantido como indicado no link acima do Github.

Conforme explicado na documentação:

  1. Adicione a dependência ao arquivo de construção de notas do seu módulo:

    dependencies {
        compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
    }
    
  2. Copie o banco de dados no diretório de ativos, em um subdiretório chamado assets/databases. Por exemplo:
    assets/databases/my_database.db

    (Opcionalmente, você pode compactar o banco de dados em um arquivo zip como assets/databases/my_database.zip. Isso não é necessário, pois o APK já está compactado como um todo.)

  3. Crie uma classe, por exemplo:

    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
fonte
O download do android-sqlite-asset-helper.jar requer qual credencial?
precisa
1
Se você estiver usando gradle, basta adicionar a dependência.
Suragch
Como você obtém dados do banco de dados?
Machado
É ainda mais fácil com o Android Studio e o gradle. Verifique o link!
bendaf
5
Observe que esta biblioteca foi abandonada, com a última atualização há 4 anos.
reduzindo a atividade
13

Minha solução não usa nenhuma biblioteca de terceiros nem obriga a chamar métodos personalizados na SQLiteOpenHelpersubclasse para inicializar o banco de dados na criação. Ele também cuida de atualizações de banco de dados também. Tudo o que precisa ser feito é a subclasse SQLiteOpenHelper.

Pré-requisito:

  1. O banco de dados que você deseja enviar com o aplicativo. Ele deve conter uma tabela 1x1 nomeada android_metadatacom um atributo localecom o valor en_US, além das tabelas exclusivas do seu aplicativo.

Subclassificação SQLiteOpenHelper:

  1. Subclasse SQLiteOpenHelper.
  2. Crie um privatemétodo dentro da SQLiteOpenHelpersubclasse. Este método contém a lógica para copiar o conteúdo do banco de dados do arquivo de banco de dados na pasta 'assets' para o banco de dados criado no contexto do pacote de aplicativos.
  3. Substituir onCreate, onUpgrade e onOpen métodos de SQLiteOpenHelper.

Disse o suficiente. Aqui está a SQLiteOpenHelpersubclasse:

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

Por fim, para obter uma conexão com o banco de dados, basta chamar getReadableDatabase()ou getWritableDatabase()na SQLiteOpenHelpersubclasse e ele cuidará da criação de um banco de dados, copiando o conteúdo do banco de dados do arquivo especificado na pasta 'assets', se o banco de dados não existir.

Em resumo, você pode usar a SQLiteOpenHelpersubclasse para acessar o banco de dados enviado na pasta assets, da mesma maneira que usaria para um banco de dados inicializado usando consultas SQL no onCreate()método.

Vaishak Nair
fonte
2
Esta é a solução mais elegante, usando APIs padrão para Androids sem a necessidade de bibliotecas externas. Como observação, não incluí a tabela android_metadata e ela funciona. As versões mais recentes do Android podem adicioná-la automaticamente.
Goetzc
12

Enviando o aplicativo com um arquivo de banco de dados, no Android Studio 3.0

Enviar o aplicativo com um arquivo de banco de dados é uma boa ideia para mim. A vantagem é que você não precisa fazer uma inicialização complexa, que às vezes custa muito tempo, se o conjunto de dados for grande.

Etapa 1: preparar o arquivo de banco de dados

Tenha seu arquivo de banco de dados pronto. Pode ser um arquivo .db ou um arquivo .sqlite. Se você usa um arquivo .sqlite, tudo que você precisa fazer é alterar os nomes de extensão de arquivo. Os passos são os mesmos.

Neste exemplo, preparei um arquivo chamado testDB.db. Possui uma tabela e alguns dados de amostra, como este insira a descrição da imagem aqui

Etapa 2: Importar o arquivo para o seu projeto

Crie a pasta de ativos se você não tiver uma. Em seguida, copie e cole o arquivo de banco de dados nessa pasta

insira a descrição da imagem aqui

Etapa 3: copie o arquivo para a pasta de dados do aplicativo

Você precisa copiar o arquivo do banco de dados para a pasta de dados do aplicativo para poder interagir mais com ele. Esta é uma ação única (inicialização) para copiar o arquivo de banco de dados. Se você chamar esse código várias vezes, o arquivo de banco de dados na pasta de dados será substituído pelo arquivo da pasta de ativos. Esse processo de substituição é útil quando você deseja atualizar o banco de dados no futuro durante a atualização do aplicativo.

Observe que durante a atualização do aplicativo, esse arquivo de banco de dados não será alterado na pasta de dados do aplicativo. Somente a desinstalação irá excluí-lo.

O arquivo do banco de dados precisa ser copiado para a /databasespasta. Abra o Gerenciador de arquivos do dispositivo. Digite a data/data/<YourAppName>/localização. Esta é a pasta de dados padrão do aplicativo mencionada acima. E, por padrão, o arquivo do banco de dados será colocado em outra pasta chamada bancos de dados nesse diretório

insira a descrição da imagem aqui

Agora, o processo de cópia de arquivo é muito parecido com o que o Java está fazendo. Use o código a seguir para copiar e colar. Este é o código de iniciação. Também pode ser usado para atualizar (substituindo) o arquivo do banco de dados no 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
}

Atualize a pasta para verificar o processo de cópia

insira a descrição da imagem aqui

Etapa 4: Criar auxiliar aberto do banco de dados

Crie uma subclasse para SQLiteOpenHelper, com connect, close, path, etc.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) {
    }
}

Etapa 5: criar classe de nível superior para interagir com o banco de dados

Essa será a classe que lê e grava seu arquivo de banco de dados. Também há uma consulta de amostra para imprimir o valor no banco de dados.

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

Etapa 6: teste em execução

Teste o código executando as seguintes linhas de códigos.

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

Aperte o botão de correr e torcer!

insira a descrição da imagem aqui

Fangming
fonte
1
quando a inicialização deve ser feita? Qual é a estratégia que você sugere?
Daniele B
8

Em novembro de 2017, o Google lançou a Biblioteca de Persistência de Sala .

A partir da documentação:

A biblioteca de persistência do Room fornece uma camada de abstração sobre o texto forte SQ Lite, para permitir acesso fluente ao banco de dados enquanto aproveita todo o poder do SQLite .

A biblioteca ajuda a criar um cache dos dados do seu aplicativo em um dispositivo que está executando seu aplicativo. Esse cache, que serve como a única fonte de verdade do seu aplicativo, permite que os usuários visualizem uma cópia consistente das principais informações do aplicativo, independentemente de os usuários terem uma conexão com a Internet.

O banco de dados da sala possui um retorno de chamada quando o banco de dados é criado ou aberto pela primeira vez. Você pode usar o criar retorno de chamada para preencher seu banco de dados.

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 desta postagem do blog .

LordRaydenMK
fonte
Obrigado, isso funcionou para mim. Exemplo de Java aqui
Jerry Sha
1
Se você deseja enviar um APK com um SQLite já existente, adicione-o à pasta assets e use este pacote github.com/humazed/RoomAsset para executar uma migração que carregará os dados do arquivo SQLite no novo. Dessa forma, você pode salvar o preenchimento de dados com um banco de dados existente.
precisa saber é o seguinte
6

Pelo que vi, você deveria estar enviando um banco de dados que já possui as tabelas e os dados de configuração. No entanto, se você quiser (e dependendo do tipo de aplicativo que você possui), poderá permitir a "opção de atualização do banco de dados". Então, o que você faz é baixar a versão mais recente do sqlite, obter as instruções mais recentes de Inserir / Criar de um arquivo de texto hospedado online, executar as instruções e fazer uma transferência de dados do db antigo para o novo.

masfenix
fonte
6
> Pelo que vi, você deveria enviar um banco de dados que já possui as tabelas e os dados de configuração. Sim, mas como você faz isso?
Rory
5

Finalmente eu consegui !! Usei este link help Usando seu próprio banco de dados SQLite em aplicativos Android , mas tive que alterá-lo um pouco.

  1. Se você possui muitos pacotes, deve colocar o nome do pacote principal aqui:

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

  2. Mudei o método que copia o banco de dados da pasta local para a pasta emulador! Ocorreu algum problema quando essa pasta não existia. Então, primeiro, verifique o caminho e, se não estiver lá, crie a pasta.

  3. No código anterior, o copyDatabasemétodo nunca foi chamado quando o banco de dados não existia e o checkDataBasemétodo causou exceção. então mudei um pouco o código.

  4. Se o seu banco de dados não tiver uma extensão de arquivo, não use o nome do arquivo com uma.

funciona bem para mim, espero que seja útil para você também

    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
fonte
u pode por favor me avise como atualizar o db se eu quiser substituir o banco de dados antigo por um novo como posso excluir o db antigo #
31514
I haven `t necessidade de fazer isso até que agora, mas se novo aplicativo foi instalado, o novo db substituir demasiado
afsane
como excluir banco de dados antigo, porque eu estou adicionando nova db na pasta ativos, em seguida, como vou apagar db idade a partir da pasta especificado de outra forma ele vai trazer o conteúdo de db velho
Erum
Espero que este seria útil stackoverflow.com/questions/9109438/...
afsane
Perfeito, obrigado! Apenas um comentário, ao lançar a exceção na verificação do banco de dados, o aplicativo é fechado, pois o banco de dados não estará lá no início e o método não continuará após o lançamento da exceção. Eu simplesmente comentei o throw new Error ("a dose do banco de dados não existe"); e agora tudo funciona perfeitamente.
Grinner 9/09/14
4

Atualmente, não há como pré-criar um banco de dados SQLite para enviar com seu apk. O melhor que você pode fazer é salvar o SQL apropriado como um recurso e executá-los no seu aplicativo. Sim, isso leva à duplicação de dados (as mesmas informações existem como um reagrupamento e como um banco de dados), mas não há outra maneira no momento. O único fator atenuante é o arquivo apk é compactado. Minha experiência é de 908 KB para menos de 268 KB.

O tópico abaixo tem a melhor discussão / solução que encontrei com um bom código de exemplo.

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

Eu armazenei minha instrução CREATE como um recurso de string para ser lido com Context.getString () e a executei com SQLiteDatabse.execSQL ().

Armazenei os dados para minhas inserções em res / raw / inserts.sql (criei o arquivo sql, mais de 7000 linhas). Usando a técnica do link acima, digitei um loop, li o arquivo linha por linha e concatenei os dados em "INSERT INTO tbl VALUE" e fiz outro SQLiteDatabase.execSQL (). Não faz sentido salvar 7000 "INSERT IN tbl VALUE" s quando eles podem ser concatenados.

Demora cerca de vinte segundos no emulador, não sei quanto tempo isso levaria em um telefone real, mas isso só acontece uma vez, quando o usuário inicia o aplicativo.

Vai
fonte
3
Que tal puxar o script SQL da Web na primeira execução? Dessa forma, não há necessidade de duplicar dados.
Tamas Czinege 6/03/09
1
Sim, mas o dispositivo precisará estar conectado à Internet. Essa é uma séria desvantagem em alguns aplicativos.
Dzhuneyt 12/02
Não faça mais de 7000 inserções, insira lotes de 100 ou mais ou menos desta forma - INSERT INTO table VALUES(...) VALUES(...) VALUES(...) ...(1 linha de inserção deve ter 100 VALUES). Será muito mais eficiente e reduzirá o tempo de inicialização de 20 segundos para 2 ou 3 segundos.
Mohit Atray 11/03
4

Enviar o banco de dados dentro do apk e copiá-lo para /data/data/...duplicará o tamanho do banco de dados (1 em apk, 1 em data/data/...) e aumentará o tamanho do apk (é claro). Portanto, seu banco de dados não deve ser muito grande.

Hiep
fonte
2
Aumenta um pouco o tamanho do apk, mas não o dobro. Quando está em ativos, é compactado e, portanto, é muito menor. Após copiá-lo para a pasta do banco de dados, ele é descompactado.
Suragch
3

O Android já fornece uma abordagem de gerenciamento de banco de dados com reconhecimento de versão. Essa abordagem foi aproveitada na estrutura BARACUS para aplicativos Android.

Permite gerenciar o banco de dados ao longo de todo o ciclo de vida da versão de um aplicativo, podendo atualizar o banco de dados sqlite de qualquer versão anterior para a atual.

Além disso, ele permite executar hot-backups e hot-recovery do SQLite.

Não tenho 100% de certeza, mas uma recuperação a quente para um dispositivo específico pode permitir que você envie um banco de dados preparado no seu aplicativo. Mas não tenho certeza sobre o formato binário do banco de dados que pode ser específico para determinados dispositivos, fornecedores ou gerações de dispositivos.

Como o material é a Licença 2 do Apache, sinta-se à vontade para reutilizar qualquer parte do código, que pode ser encontrada no github

EDIT:

Se você deseja enviar dados apenas, considere instanciar e persistir POJOs no primeiro início do aplicativo. O BARACUS recebeu um suporte interno para isso (armazenamento de valores-chave interno para informações de configuração, por exemplo, "APP_FIRST_RUN" mais um gancho de inicialização após o contexto para executar operações pós-inicialização no contexto). Isso permite que você envie dados acoplados estreitos com seu aplicativo; na maioria dos casos, isso se encaixava nos meus casos de uso.

gorefest
fonte
3

Se os dados necessários não forem muito grandes (os limites que eu não conheço, dependerão de muitas coisas), você também poderá fazer o download dos dados (em XML, JSON, o que for) de um site / aplicativo da web. Após receber, execute as instruções SQL usando os dados recebidos, criando suas tabelas e inserindo os dados.

Se o seu aplicativo móvel contiver muitos dados, poderá ser mais fácil posteriormente atualizar os dados nos aplicativos instalados com dados ou alterações mais precisas.

Jaco
fonte
3

Modifiquei a classe e as respostas para a pergunta e escrevi uma classe que permite atualizar o banco de dados via DB_VERSION.

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

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

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

        copyDataBase();

        this.getReadableDatabase();
    }

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

            copyDataBase();

            mNeedUpdate = false;
        }
    }

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

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

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

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

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

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

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

Usando uma classe.

Na classe de atividade, declare variáveis.

private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;

No método onCreate, escreva o seguinte 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;
}

Se você adicionar um arquivo de banco de dados à pasta res / raw, use a seguinte modificação da classe.

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

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

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

        copyDataBase();

        this.getReadableDatabase();
    }

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

            copyDataBase();

            mNeedUpdate = false;
        }
    }

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

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

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

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

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

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

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

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

Harrix
fonte
2

Eu escrevi uma biblioteca para simplificar esse processo.

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

Ele criará um banco de dados a partir do assets/databases/myDb.dbarquivo. Além disso, você terá todas essas funcionalidades:

  • Carregar banco de dados do arquivo
  • Acesso sincronizado ao banco de dados
  • Usando sqlite-android por requery, distribuição específica do Android das últimas versões do SQLite.

Clone-o no github .

Ilya Gazman
fonte
2

Estou usando o ORMLite e o código abaixo funcionou para mim

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

    }
}

Observe que o código extrai o arquivo de banco de dados de um arquivo zip nos ativos

Homayoun Behzadian
fonte