Kirimkan aplikasi dengan database

959

Jika aplikasi Anda membutuhkan database dan disertai dengan data bawaan, apa cara terbaik untuk mengirimkan aplikasi itu? Haruskah saya:

  1. Membuat ulang database SQLite dan memasukkannya ke dalam .apk?

  2. Sertakan perintah SQL dengan aplikasi dan minta dibuat database dan masukkan data pada penggunaan pertama?

Kelemahan yang saya lihat adalah:

  1. Kemungkinan ketidaksesuaian versi SQLite dapat menyebabkan masalah dan saya saat ini tidak tahu ke mana database harus pergi dan bagaimana mengaksesnya.

  2. Mungkin perlu waktu sangat lama untuk membuat dan mengisi basis data pada perangkat.

Ada saran? Pointer ke dokumentasi tentang masalah apa pun akan sangat dihargai.

Heikki Toivonen
sumber
6
gunakan SQLiteAssetHelper
Richard Le Mesurier

Jawaban:

199

Ada dua opsi untuk membuat dan memperbarui basis data.

Salah satunya adalah membuat database secara eksternal, lalu menempatkannya di folder aset proyek dan kemudian menyalin seluruh database dari sana. Ini jauh lebih cepat jika database memiliki banyak tabel dan komponen lainnya. Peningkatan dipicu dengan mengubah nomor versi database dalam file res / values ​​/ strings.xml. Upgrade kemudian akan dilakukan dengan membuat database baru secara eksternal, mengganti database lama di folder aset dengan database baru, menyimpan database lama di penyimpanan internal dengan nama lain, menyalin database baru dari folder aset ke penyimpanan internal, mentransfer semua dari data dari database lama (yang diubah namanya sebelumnya) ke dalam database baru dan akhirnya menghapus database lama. Anda dapat membuat database dengan menggunakanPlugin SQLite Manager FireFox untuk mengeksekusi pernyataan sql kreasi Anda.

Pilihan lainnya adalah membuat database secara internal dari file sql. Ini tidak secepat tetapi penundaan mungkin tidak akan terlihat oleh pengguna jika database hanya memiliki beberapa tabel. Peningkatan dipicu dengan mengubah nomor versi database dalam file res / values ​​/ strings.xml. Upgrade kemudian akan dilakukan dengan memproses file sql upgrade. Data dalam database akan tetap tidak berubah kecuali ketika wadahnya dihapus, misalnya menjatuhkan tabel.

Contoh di bawah ini menunjukkan cara menggunakan kedua metode tersebut.

Berikut adalah contoh file create_database.sql. Itu akan ditempatkan di folder aset proyek untuk metode internal atau disalin ke "Execute SQL 'dari SQLite Manager untuk membuat database untuk metode eksternal. (CATATAN: Perhatikan komentar tentang tabel yang diperlukan oleh 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"; 

Ini adalah contoh file update_database.sql. Itu akan ditempatkan di folder aset proyek untuk metode internal atau disalin ke "Execute SQL 'dari SQLite Manager untuk membuat database untuk metode eksternal. (CATATAN: Perhatikan bahwa ketiga jenis komentar SQL akan diabaikan oleh parser sql yang termasuk dalam contoh ini.)

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

Berikut adalah entri untuk menambahkan ke file /res/values/strings.xml untuk nomor versi database.

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

Berikut adalah aktivitas yang mengakses database dan kemudian menggunakannya. ( Catatan: Anda mungkin ingin menjalankan kode database di utas terpisah jika menggunakan banyak sumber daya. )

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

    }
}

Berikut adalah kelas pembantu database di mana database dibuat atau diperbarui jika perlu. (CATATAN: Android mengharuskan Anda membuat kelas yang memperluas SQLiteOpenHelper untuk bekerja dengan database Sqlite.)

package android.example;

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

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

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

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

    private final Context myContext;

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

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

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

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

    }

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

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

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

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

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

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

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

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

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

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

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

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

}

Berikut kelas FileHelper yang berisi metode untuk menyalin file byte stream dan parsing file sql.

package android.example;

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

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

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

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

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

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

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

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

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

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

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

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

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

        }

        sqlFile.close();

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

}
Danny Remington - OMS
sumber
saya menggunakan kode di atas untuk meng-upgrade db saya "upgrade_database.sql berisi pernyataan insert. beberapa nilai memiliki titik koma seperti memasukkan nilai table_a ('ss', 'ddd', 'aaaa; aaa');" ketika saya menjalankan saya perhatikan di atas menyebutkan masukkan tidak mendapatkan esecute karena titik koma di nilai setiap ides bagaimana cara memperbaikinya.
Sam
5
Ada opsi ketiga - salin db dari web. Saya telah melakukan ini dan ini berjalan cukup cepat untuk 4 MB db. Ini juga memecahkan masalah dengan 2.3, di mana solusi pertama (copy db) tidak berfungsi.
Jack BeNimble
2
Danny And Austyn - Solusi Anda sempurna. Saya mengalami masalah dengan solusi diseduh rumah saya dan tersandung pada Anda. Itu benar-benar menghantam tempat. Terima kasih telah meluangkan waktu untuk menyediakannya.
George Baker
4
Saya lebih suka jawaban ini daripada yang terpilih dan diterima. Ini memiliki semua informasi di satu tempat (tidak melihat bagian tautan ini) dan menyebutkan beberapa spesifikasi Android yang saya tidak tahu ada (seperti CREATE TABLE "android_metadata"). Juga contoh ditulis dengan sangat rinci yang merupakan nilai tambah. Ini hampir merupakan solusi salin tempel yang tidak selalu baik tetapi penjelasan antar kode sangat bagus.
Igor Čordaš
Saya menggunakan metode yang sama tetapi saya menghadapi satu masalah. Bagaimana kita bisa menyalin semua data yang ada dari file db lama ke baru dengan cara yang lebih mudah.
Pankaj
130

The SQLiteAssetHelperlibrary membuat tugas ini benar-benar sederhana.

Sangat mudah untuk ditambahkan sebagai dependensi gradle (tetapi Jar juga tersedia untuk Ant / Eclipse), dan bersama dengan dokumentasinya dapat ditemukan di:
https://github.com/jgilfelt/android-sqlite-asset-helper

Catatan: Proyek ini tidak lagi dikelola seperti yang disebutkan pada tautan Github di atas.

Sebagaimana dijelaskan dalam dokumentasi:

  1. Tambahkan dependensi ke file build gradle modul Anda:

    dependencies {
        compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
    }
    
  2. Salin database ke direktori aset, di subdirektori bernama assets/databases. Contohnya:
    assets/databases/my_database.db

    (Secara opsional, Anda dapat mengompres database dalam file zip seperti assets/databases/my_database.zip. Ini tidak diperlukan, karena APK sudah dikompresi secara keseluruhan.)

  3. Buat kelas, misalnya:

    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
sumber
Pengunduhan android-sqlite-asset-helper.jar memerlukan kredensial mana?
Pr38y
1
Jika Anda menggunakan gradle maka Anda tinggal menambahkan ketergantungan.
Suragch
Bagaimana Anda mendapatkan data dari DB?
Machado
Bahkan lebih mudah dengan Android Studio dan gradle. Periksa tautannya!
bendaf
5
Perhatikan bahwa perpustakaan ini ditinggalkan, dengan pembaruan terakhir 4 tahun yang lalu.
Mengurangi aktivitas
13

Solusi saya tidak menggunakan pustaka pihak ketiga atau memaksa Anda untuk memanggil metode kustom pada SQLiteOpenHelpersubkelas untuk menginisialisasi database saat pembuatan. Ini juga menangani peningkatan basis data juga. Yang perlu dilakukan hanyalah subclass SQLiteOpenHelper.

Prasyarat:

  1. Basis data yang ingin Anda kirim dengan aplikasi. Seharusnya berisi tabel 1x1 bernama android_metadatadengan atribut yang localememiliki nilai en_USselain tabel yang unik untuk aplikasi Anda.

Subklasifikasi SQLiteOpenHelper:

  1. Subkelas SQLiteOpenHelper.
  2. Buat privatemetode dalam SQLiteOpenHelpersubclass. Metode ini berisi logika untuk menyalin konten database dari file database di folder 'aset' ke database yang dibuat dalam konteks paket aplikasi.
  3. Mengganti onCreate, onUpgrade dan onOpen metode SQLiteOpenHelper.

Cukup kata. Ini dia SQLiteOpenHelpersubclass:

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

Akhirnya, untuk mendapatkan koneksi basis data, panggil saja getReadableDatabase()atau getWritableDatabase()pada SQLiteOpenHelpersubkelas dan itu akan mengurus pembuatan db, menyalin konten db dari file yang ditentukan dalam folder 'aset', jika database tidak ada.

Singkatnya, Anda dapat menggunakan SQLiteOpenHelpersubkelas untuk mengakses db yang dikirimkan dalam folder aset sama seperti yang Anda gunakan untuk database yang diinisialisasi dengan menggunakan query SQL dalam onCreate()metode ini.

Vaishak Nair
sumber
2
Ini adalah solusi paling elegan, menggunakan API Android standar tanpa perlu perpustakaan eksternal. Sebagai catatan, saya tidak menyertakan tabel android_metadata dan berfungsi, versi Android yang lebih baru mungkin menambahkannya secara otomatis.
goetzc
12

Mengirim aplikasi dengan file database, di Android Studio 3.0

Mengirim aplikasi dengan file database adalah ide yang bagus untuk saya. Keuntungannya adalah Anda tidak perlu melakukan inisialisasi yang kompleks, yang terkadang menghabiskan banyak waktu, jika kumpulan data Anda sangat besar.

Langkah 1: Siapkan file database

Siapkan file database Anda. Ini bisa berupa file .db atau .sqlite. Jika Anda menggunakan file .sqlite, yang perlu Anda lakukan adalah mengubah nama ekstensi file. Langkah-langkahnya sama.

Dalam contoh ini, saya menyiapkan file bernama testDB.db. Ini memiliki satu tabel dan beberapa sampel data di dalamnya seperti ini masukkan deskripsi gambar di sini

Langkah 2: Impor file ke proyek Anda

Buat folder aset jika Anda belum memilikinya. Kemudian salin dan tempel file database ke folder ini

masukkan deskripsi gambar di sini

Langkah 3: Salin file ke folder data aplikasi

Anda perlu menyalin file database ke folder data aplikasi untuk melakukan interaksi lebih lanjut dengannya. Ini adalah tindakan satu kali (inisialisasi) untuk menyalin file database. Jika Anda memanggil kode ini beberapa kali, file database di folder data akan ditimpa oleh yang ada di folder aset. Proses menimpa ini berguna ketika Anda ingin memperbarui basis data di masa depan selama pembaruan aplikasi.

Perhatikan bahwa selama pembaruan aplikasi, file database ini tidak akan diubah di folder data aplikasi. Hanya uninstall yang akan menghapusnya.

File database perlu disalin ke /databasesfolder. Buka Device File Explorer. Masukkan data/data/<YourAppName>/lokasi. Ini adalah folder data default aplikasi yang disebutkan di atas. Dan secara default, file database akan ditempatkan di folder lain yang disebut database di bawah direktori ini

masukkan deskripsi gambar di sini

Sekarang, proses menyalin file cukup banyak seperti apa yang dilakukan Java. Gunakan kode berikut untuk melakukan copy paste. Ini adalah kode inisiasi. Itu juga dapat digunakan untuk memperbarui (dengan menimpa) file database di masa depan.

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

Kemudian segarkan folder untuk memverifikasi proses penyalinan

masukkan deskripsi gambar di sini

Langkah 4: Buat database open helper

Buat subclass untuk SQLiteOpenHelper, dengan connect, close, path, dll. Saya menamainyaDatabaseOpenHelper

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

Langkah 5: Buat kelas tingkat atas untuk berinteraksi dengan database

Ini akan menjadi kelas yang membaca & menulis file database Anda. Juga ada permintaan sampel untuk mencetak nilai dalam database.

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

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

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

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

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

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

Langkah 6: Uji coba

Uji kode dengan menjalankan baris kode berikut.

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

Tekan tombol run dan bersoraklah!

masukkan deskripsi gambar di sini

Fangming
sumber
1
kapan inisialisasi harus dilakukan? Apa strategi yang Anda sarankan?
Daniele B
8

Pada November 2017 Google merilis Perpustakaan Persistence Room .

Dari dokumentasi:

Perpustakaan kegigihan Kamar menyediakan lapisan abstraksi lebih dari SQ Lite teks yang kuat untuk memungkinkan akses database yang lancar sambil memanfaatkan kekuatan penuh SQLite .

Perpustakaan membantu Anda membuat cache data aplikasi Anda pada perangkat yang menjalankan aplikasi Anda. Cache ini, yang berfungsi sebagai satu-satunya sumber kebenaran aplikasi Anda, memungkinkan pengguna untuk melihat salinan informasi kunci yang konsisten dalam aplikasi Anda, terlepas dari apakah pengguna memiliki koneksi internet.

Database Kamar memiliki panggilan balik ketika database pertama kali dibuat atau dibuka. Anda dapat menggunakan buat panggilan balik untuk mengisi basis data Anda.

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

Kode dari posting blog ini .

LordRaydenMK
sumber
Terima kasih, ini berhasil untuk saya. Contoh Java di sini
Jerry Sha
1
Jika Anda ingin mengirim APK dengan SQLite yang sudah ada, Anda dapat menambahkannya ke folder aset dan menggunakan paket ini github.com/humazed/RoomAsset untuk melakukan migrasi yang akan memuat data file SQLite ke yang baru. Dengan cara ini, Anda dapat menyimpan populasi data dengan DB yang ada.
xarlymg89
6

Dari apa yang saya lihat Anda harus mengirim database yang sudah memiliki pengaturan tabel dan data. Namun jika Anda menginginkannya (dan tergantung pada jenis aplikasi yang Anda miliki), Anda dapat mengizinkan "pemutakhiran opsi basis data". Kemudian yang Anda lakukan adalah mengunduh versi sqlite terbaru, dapatkan pernyataan Sisipkan / Buat terbaru dari file teks yang dihosting online, jalankan pernyataan dan lakukan transfer data dari db lama ke yang baru.

masfenix
sumber
6
> Dari apa yang saya lihat Anda seharusnya mengirim database yang sudah memiliki pengaturan tabel dan data. Ya tetapi bagaimana Anda melakukan ini?
Rory
5

Akhirnya saya berhasil !! Saya telah menggunakan bantuan tautan ini Menggunakan database SQLite Anda sendiri di aplikasi Android , tetapi harus mengubahnya sedikit.

  1. Jika Anda memiliki banyak paket, Anda harus meletakkan nama paket master di sini:

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

  2. Saya mengubah metode yang menyalin database dari folder lokal ke folder emulator! Ada beberapa masalah ketika folder itu tidak ada. Jadi pertama-tama, itu harus memeriksa jalan dan jika tidak ada, itu harus membuat folder.

  3. Dalam kode sebelumnya, copyDatabasemetode tidak pernah dipanggil ketika database tidak ada dan checkDataBasemetode ini menyebabkan pengecualian. jadi saya mengubah sedikit kode.

  4. Jika database Anda tidak memiliki ekstensi file, jangan gunakan nama file dengan ekstensi file.

itu bekerja baik untuk saya, saya harap ini akan berguna bagi Anda juga

    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
sumber
bisakah Anda memberi tahu saya cara memutakhirkan db jika saya ingin mengganti database lama dengan yang baru bagaimana saya bisa menghapus db lama
Erum
Saya belum perlu melakukan ini sampai sekarang, tetapi jika aplikasi baru telah diinstal, db baru juga diganti
afsane
cara menghapus database lama karena saya menambahkan db baru di folder aset lalu bagaimana saya akan menghapus db lama dari folder yang ditentukan jika tidak maka akan membawa isi db lama
Erum
Saya harap ini akan berguna stackoverflow.com/questions/9109438/…
afsane
Sempurna, terima kasih! Hanya satu komentar, melempar pengecualian pada pengecekan basis data menyebabkan aplikasi ditutup, karena DB tidak akan ada di awal dan metode tidak berlanjut setelah pengecualian dilemparkan. Saya hanya berkomentar melempar Kesalahan baru ("dosis basis data tidak ada"); dan sekarang semuanya bekerja dengan sempurna.
Grinner
4

Saat ini tidak ada cara untuk membuat database SQLite untuk dikirimkan dengan apk Anda. Yang terbaik yang dapat Anda lakukan adalah menyimpan SQL yang sesuai sebagai sumber daya dan menjalankannya dari aplikasi Anda. Ya, ini mengarah pada duplikasi data (informasi yang sama ada sebagai resrouce dan sebagai database) tetapi tidak ada cara lain saat ini. Satu-satunya faktor yang meringankan adalah file apk dikompresi. Pengalaman saya adalah kompres 908KB menjadi kurang dari 268KB.

Utas di bawah ini memiliki diskusi / solusi terbaik yang saya temukan dengan kode sampel yang baik.

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

Saya menyimpan pernyataan CREATE saya sebagai sumber daya string untuk dibaca dengan Context.getString () dan menjalankannya dengan SQLiteDatabse.execSQL ().

Saya menyimpan data untuk sisipan saya di res / raw / inserts.sql (saya membuat file sql, 7000+ baris). Dengan menggunakan teknik dari tautan di atas, saya memasukkan loop, membaca file baris demi baris dan menggabungkan data ke "INSERT INTO tbl VALUE" dan melakukan SQLiteDatabase.execSQL () lainnya. Tidak ada gunanya menyimpan 7000 "INSERT INTO tbl VALUE" ketika mereka baru saja dikonsolidasikan.

Dibutuhkan sekitar dua puluh detik pada emulator, saya tidak tahu berapa lama ini akan dilakukan pada telepon nyata, tetapi itu hanya terjadi sekali, ketika pengguna pertama kali memulai aplikasi.

Akan
sumber
3
Bagaimana dengan menarik skrip SQL dari web saat dijalankan pertama kali? Dengan cara ini tidak perlu menggandakan data.
Tamas Czinege
1
Ya, tetapi perangkat harus terhubung ke internet. Itu adalah kelemahan serius dalam beberapa aplikasi.
Dzhuneyt
Jangan melakukan 7000+ sisipan, lakukan sisipan batch 100 atau lebih seperti ini - INSERT INTO table VALUES(...) VALUES(...) VALUES(...) ...(1 baris sisipan harus memiliki 100 NILAI). Ini akan jauh lebih efisien dan akan mengurangi waktu startup Anda dari 20 detik menjadi 2 atau 3 detik.
Mohit Atray
4

Mengirim database di dalam apk dan kemudian menyalinnya /data/data/...akan menggandakan ukuran database (1 di apk, 1 di data/data/...), dan akan meningkatkan ukuran apk (tentu saja). Jadi database Anda tidak boleh terlalu besar.

Hai
sumber
2
Itu memang meningkatkan ukuran apk tetapi tidak menggandakannya. Ketika dalam aset itu dikompresi dan jadi jauh lebih kecil. Setelah menyalinnya ke folder database itu menjadi tidak terkompresi.
Suragch
3

Android sudah menyediakan pendekatan manajemen basis data versi sadar. Pendekatan ini telah dimanfaatkan dalam kerangka kerja BARACUS untuk aplikasi Android.

Ini memungkinkan Anda untuk mengelola basis data di sepanjang siklus seluruh versi aplikasi, karena dapat memperbarui basis data sqlite dari versi sebelumnya ke versi saat ini.

Juga, ini memungkinkan Anda untuk menjalankan hot-backup dan hot-recovery dari SQLite.

Saya tidak 100% yakin, tetapi pemulihan panas untuk perangkat tertentu memungkinkan Anda untuk mengirim basis data yang sudah disiapkan di aplikasi Anda. Tetapi saya tidak yakin tentang format biner basis data yang mungkin spesifik untuk perangkat, vendor, atau generasi perangkat tertentu.

Karena masalahnya adalah Apache License 2, jangan ragu untuk menggunakan kembali bagian mana pun dari kode, yang dapat ditemukan di github

EDIT:

Jika Anda hanya ingin mengirimkan data, Anda dapat mempertimbangkan untuk memulai dan bertahan POJO pada aplikasi pertama kali mulai. BARACUS mendapat dukungan bawaan untuk ini (penyimpanan nilai kunci bawaan untuk info konfigurasi, mis. "APP_FIRST_RUN" ditambah pengait bootstrap setelah konteks untuk menjalankan operasi pasca peluncuran pada konteks). Ini memungkinkan Anda untuk memiliki data yang digabungkan ketat dengan aplikasi Anda; dalam banyak kasus ini cocok dengan kasus penggunaan saya.

gorefest
sumber
3

Jika data yang diperlukan tidak terlalu besar (batas yang saya tidak tahu, akan tergantung pada banyak hal), Anda juga dapat mengunduh data (dalam XML, JSON, apa pun) dari situs web / webapp. Setelah menerima, jalankan pernyataan SQL menggunakan data yang diterima membuat tabel Anda dan memasukkan data.

Jika aplikasi seluler Anda berisi banyak data, nanti akan lebih mudah untuk memperbarui data di aplikasi yang diinstal dengan data atau perubahan yang lebih akurat.

Jaco
sumber
3

Saya memodifikasi kelas dan jawaban untuk pertanyaan dan menulis kelas yang memungkinkan memperbarui basis data melalui 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;
    }
}

Menggunakan kelas.

Di kelas aktivitas, deklarasikan variabel.

private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;

Dalam metode onCreate, tulis kode berikut.

mDBHelper = new DatabaseHelper(this);

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

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

Jika Anda menambahkan file database ke folder res / raw kemudian gunakan modifikasi kelas berikut.

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
sumber
2

Saya menulis perpustakaan untuk menyederhanakan proses ini.

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

Ini akan membuat dataBase dari assets/databases/myDb.dbfile. Selain itu, Anda akan mendapatkan semua fungsionalitas itu:

  • Muat basis data dari file
  • Akses tersinkronisasi ke database
  • Menggunakan sqlite-android dengan permintaan, distribusi spesifik Android dari versi terbaru SQLite.

Kloning dari github .

Ilya Gazman
sumber
2

Saya menggunakan ORMLite dan kode di bawah ini berfungsi untuk saya

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

    }
}

Harap dicatat, Kode mengekstrak file database dari file zip di aset

Homayoun Behzadian
sumber