繁体   English   中英

发布带有数据库的应用程序

[英]Ship an application with a database

如果您的应用程序需要一个数据库并且它带有内置数据,那么发布该应用程序的最佳方式是什么? 我是不是该:

  1. 预创建 SQLite 数据库并将其包含在.apk中?

  2. 在应用程序中包含 SQL 命令并让它创建数据库并在首次使用时插入数据?

我看到的缺点是:

  1. 可能的 SQLite 版本不匹配可能会导致问题,我目前不知道数据库应该在哪里 go 以及如何访问它。

  2. 在设备上创建和填充数据库可能需要很长时间。

有什么建议么? 指向有关任何问题的文档的指针将不胜感激。

我刚刚在ReignDesign blog中找到了一种在Android应用程序中使用自己的SQLite数据库的文章。 基本上你预先创建数据库,将它放在apk的assets目录中,并首先使用copy到/data/data/YOUR_PACKAGE/databases/目录。

创建和更新数据库有两个选项。

一种是在外部创建一个数据库,然后将其放在项目的assets文件夹中,然后从那里复制整个数据库。 如果数据库有很多表和其他组件,这会快得多。 通过更改 res/values/strings.xml 文件中的数据库版本号来触发升级。 然后通过在外部创建一个新数据库,用新数据库替换资产文件夹中的旧数据库,以另一个名称将旧数据库保存在内部存储中,将新数据库从资产文件夹复制到内部存储中,转移所有将旧数据库(之前重命名的)中的数据转移到新数据库中,最后删除旧数据库。 您可以通过使用SQLite Manager FireFox 插件执行您的创建 sql 语句来创建数据库。

另一种选择是从 sql 文件内部创建数据库。 这不是那么快,但如果数据库只有几个表,那么延迟可能不会被用户注意到。 通过更改 res/values/strings.xml 文件中的数据库版本号来触发升级。 然后将通过处理升级 sql 文件来完成升级。 数据库中的数据将保持不变,除非删除其容器,例如删除表。

下面的示例演示了如何使用任一方法。

这是一个示例 create_database.sql 文件。 内部方法要放在项目的assets文件夹中,或者复制到SQLite Manager的“执行SQL”中,为外部方法创建数据库。 (注意:注意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"; 

这是一个示例 update_database.sql 文件。 内部方法要放在项目的assets文件夹中,或者复制到SQLite Manager的“执行SQL”中,为外部方法创建数据库。 (注意:三种SQL注释都会被忽略通过本示例中包含的 sql 解析器。)

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

这是要添加到 /res/values/strings.xml 文件的数据库版本号条目。

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

这是一个访问数据库然后使用它的活动。 注意:如果数据库代码使用大量资源,您可能希望在单独的线程中运行它。

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

    }
}

这是数据库助手类,如果需要,可以在其中创建或更新数据库。 (注意:Android 要求您创建一个扩展 SQLiteOpenHelper 的类,以便使用 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.
     */

}

这是 FileHelper 类,它包含字节流复制文件和解析 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(";");
    }

}

SQLiteAssetHelper库让这个任务变得非常简单。

很容易添加为 gradle 依赖项(但 Jar 也可用于 Ant/Eclipse),并且可以在以下位置找到它的文档:
https://github.com/jgilfelt/android-sqlite-asset-helper

注意:该项目不再按照上述 Github 链接进行维护。

如文档中所述:

  1. 将依赖项添加到模块的 gradle 构建文件中:

     dependencies { compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+' }
  2. 将数据库复制到资产目录中,位于名为assets/databases的子目录中。 例如:
    assets/databases/my_database.db

    (或者,您可以将数据库压缩在一个 zip 文件中,例如assets/databases/my_database.zip 。这不是必需的,因为 APK 已经作为一个整体进行了压缩。)

  3. 创建一个类,例如:

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

我猜直到今天最好的和最新的方式是使用SQLiteAssetHelper类。

本教程可完美指导您在Android中导入和使用外部数据库

Android SQLiteAssetHelper库允许您在台式计算机中构建SQLite数据库,并在Android应用程序中导入和使用它。 让我们创建一个简单的应用程序来演示该库的应用程序。

第1步 :使用您喜欢的SQLite数据库应用程序创建数据库quotes.db(用于SQLite的数据库浏览器是一个可移植的跨平台免费软件,可用于创建和编辑SQLite数据库)。 用单列“ quote”创建一个表“ quotes”。 在表“ quotes”中插入一些随机引号。

步骤2 :可以将数据库直接原样或作为压缩文件导入到项目中。 如果您的数据库太大,则建议使用压缩文件。 您可以创建ZIP压缩或GZ压缩。

压缩数据库文件的文件名必须是quotes.db.zip ,如果您正在使用ZIP压缩或quotes.db.gz ,如果您正在使用GZ压缩。

步骤3 :使用包名称com.javahelps.com.javahelps.externaldatabasedemo创建一个新的应用程序External Database Demo

步骤4 :打开build.gradle (模块:app)文件并添加以下依赖项。

 dependencies { compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+' } 

保存build.gradle文件后,单击“立即同步”链接以更新项目。 您可以同步build.gradle通过右键点击, build.gradle文件并选择Synchronize build.gradle选项也是如此。

第5步 :右键单击app文件夹并创建新的资产文件夹。

步骤6 :在资产文件夹内创建一个新的文件夹“数据库”。

步骤7 :将quotes.db.zip文件复制并粘贴到assets/databases文件夹内。

步骤8 :创建一个新类DatabaseOpenHelper

 package com.javahelps.externaldatabasedemo; import android.content.Context; import com.readystatesoftware.sqliteasset.SQLiteAssetHelper; public class DatabaseOpenHelper extends SQLiteAssetHelper { private static final String DATABASE_NAME = "quotes.db"; private static final int DATABASE_VERSION = 1; public DatabaseOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } } Notice that rather than extending SQLiteOpenHelper, the DatabaseOpenHelper extends SQLiteAssetHelper class. 

步骤9 :创建一个新的类DatabaseAccess然后输入如下所示的代码。 有关此类的更多详细信息,请参见“高级Android数据库”教程。

 package com.javahelps.externaldatabasedemo; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import java.util.ArrayList; import java.util.List; public class DatabaseAccess { private SQLiteOpenHelper openHelper; private SQLiteDatabase database; private static DatabaseAccess instance; /** * Private constructor to aboid object creation from outside classes. * * @param context */ private DatabaseAccess(Context context) { this.openHelper = new DatabaseOpenHelper(context); } /** * Return a singleton instance of DatabaseAccess. * * @param context the Context * @return the instance of DabaseAccess */ public static DatabaseAccess getInstance(Context context) { if (instance == null) { instance = new DatabaseAccess(context); } return instance; } /** * Open the database connection. */ public void open() { this.database = openHelper.getWritableDatabase(); } /** * Close the database connection. */ public void close() { if (database != null) { this.database.close(); } } /** * Read all quotes from the database. * * @return a List of quotes */ public List<String> getQuotes() { List<String> list = new ArrayList<>(); Cursor cursor = database.rawQuery("SELECT * FROM quotes", null); cursor.moveToFirst(); while (!cursor.isAfterLast()) { list.add(cursor.getString(0)); cursor.moveToNext(); } cursor.close(); return list; } } In this class only the `getQuotes` method is implemented to read the data from the database. You have the full freedom to insert, 

照常更新和删除数据库中的任何行。 有关更多详细信息,请单击此链接高级Android数据库。

与数据库有关的所有设置均已完成,现在我们需要创建一个ListView来显示报价。

步骤10 :在activity_main.xml添加一个ListView

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" /> </FrameLayout> 

步骤11 :在MainActivityonCreate方法中找到ListView对象,并提供从数据库读取的引号。

 package com.javahelps.externaldatabasedemo; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.widget.ArrayAdapter; import android.widget.ListView; import java.util.List; public class MainActivity extends ActionBarActivity { private ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.listView = (ListView) findViewById(R.id.listView); DatabaseAccess databaseAccess = DatabaseAccess.getInstance(this); databaseAccess.open(); List<String> quotes = databaseAccess.getQuotes(); databaseAccess.close(); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, quotes); this.listView.setAdapter(adapter); } } 

步骤12 :保存所有更改并运行应用程序。

除了本文之外,您还可以在此处下载SQLiteAssetHelper

在 Android Studio 3.0 中使用数据库文件传送应用程序

将应用程序与数据库文件一起发送对我来说是个好主意。 优点是你不需要做复杂的初始化,如果你的数据集很大,这有时会花费很多时间。

第一步:准备数据库文件

准备好数据库文件。 它可以是 .db 文件或 .sqlite 文件。 如果您使用 .sqlite 文件,您需要做的就是更改文件扩展名。 步骤是一样的。

在这个例子中,我准备了一个名为 testDB.db 的文件。 它有一张表和一些示例数据,如下所示在此处输入图片说明

第 2 步:将文件导入到您的项目中

如果您还没有,请创建资产文件夹。 然后将数据库文件复制并粘贴到此文件夹中

在此处输入图片说明

第 3 步:将文件复制到应用程序的数据文件夹

您需要将数据库文件复制到应用程序的数据文件夹中,以便与它进行进一步的交互。 这是复制数据库文件的一次性操作(初始化)。 如果多次调用此代码,data 文件夹中的数据库文件将被 assets 文件夹中的数据库文件覆盖。 当您希望在应用程序更新期间更新数据库时,此覆盖过程非常有用。

请注意,在应用更新期间,该数据库文件不会在应用的数据文件夹中更改。 只有卸载才会删除它。

需要将数据库文件复制到/databases文件夹。 打开设备文件资源管理器。 输入data/data/<YourAppName>/位置。 这是上面提到的应用程序的默认数据文件夹。 默认情况下,数据库文件将放置在该目录下另一个名为 databases 的文件夹中

在此处输入图片说明

现在,复制文件过程与 Java 所做的非常相似。 使用以下代码进行复制粘贴。 这是启动代码。 它还可以用于将来更新(通过覆盖)数据库文件。

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

然后刷新文件夹以验证复制过程

在此处输入图片说明

第四步:创建数据库打开助手

SQLiteOpenHelper创建一个子类,包括连接、关闭、路径等。我将其命名为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) {
    }
}

第 5 步:创建顶级类以与数据库交互

这将是读取和写入数据库文件的类。 还有一个示例查询可以打印出数据库中的值。

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

第六步:试运行

通过运行以下代码行来测试代码。

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

按下运行按钮,欢呼吧!

在此处输入图片说明

我的解决方案既不使用任何第三方库,也不强制您调用SQLiteOpenHelper子类上的自定义方法来在创建时初始化数据库。 它还负责数据库升级。 需要做的就是SQLiteOpenHelper

先决条件:

  1. 您希望随应用程序一起提供的数据库。 它应该包含一个名为android_metadata的 1x1 表,除了您的应用程序独有的表外,还包含一个值为en_US的属性locale

子类化SQLiteOpenHelper

  1. 子类SQLiteOpenHelper
  2. SQLiteOpenHelper子类中创建一个private方法。 此方法包含将数据库内容从“assets”文件夹中的数据库文件复制到在应用程序包上下文中创建的数据库的逻辑。
  3. 覆盖SQLiteOpenHelper onCreateonUpgradeonOpen方法。

说够了。 这是SQLiteOpenHelper子类:

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

最后,要获得数据库连接,只需在SQLiteOpenHelper子类上调用getReadableDatabase()getWritableDatabase() ,它将负责创建数据库,从“资产”文件夹中的指定文件复制数据库内容,如果数据库没有存在。

简而言之,您可以使用SQLiteOpenHelper子类来访问资产文件夹中提供的数据库,就像您在onCreate()方法中使用 SQL 查询初始化的数据库一样。

2017 年 11 月,Google 发布了Room Persistence Library

从文档:

Room 持久性库在 SQLite 上提供了一个抽象层,以允许流畅的数据库访问,同时利用SQLite的全部功能。

该库可帮助您在运行您的应用程序的设备上创建应用程序数据的缓存。 此缓存作为您应用的唯一真实来源,允许用户查看您应用内关键信息的一致副本,无论用户是否有互联网连接。

Room 数据库在第一次创建或打开数据库时有一个回调。 您可以使用 create 回调来填充您的数据库。

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

来自这篇博文的代码。

从我所看到的,你应该发送一个已经有表设置和数据的数据库。 但是,如果您愿意(并且取决于您拥有的应用程序类型),您可以允许“升级数据库选项”。 然后你要做的是下载最新的 sqlite 版本,获取在线托管的文本文件的最新插入/创建语句,执行这些语句并将数据从旧数据库传输到新数据库。

目前没有办法预先创建一个 SQLite 数据库来与您的 apk 一起提供。 您能做的最好的事情是将适当的 SQL 保存为资源并从您的应用程序中运行它们。 是的,这会导致数据重复(相同的信息作为资源和数据库存在),但目前没有其他方法。 唯一的缓解因素是 apk 文件被压缩。 我的经验是 908KB 压缩到小于 268KB。

下面的线程具有我发现的最佳示例代码的最佳讨论/解决方案。

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

我将我的 CREATE 语句存储为一个字符串资源,以便使用 Context.getString() 读取并使用 SQLiteDatabse.execSQL() 运行它。

我将插入的数据存储在 res/raw/inserts.sql 中(我创建了 sql 文件,7000 多行)。 使用上面链接中的技术,我进入了一个循环,逐行读取文件并将数据合并到“INSERT INTO tbl VALUE”上,然后执行另一个 SQLiteDatabase.execSQL()。 当它们可以连接时,节省 7000 个“INSERT INTO tbl VALUE”是没有意义的。

在模拟器上大约需要 20 秒,我不知道这在真正的手机上需要多长时间,但它只发生一次,当用户第一次启动应用程序时。

我终于做到了!! 我已经使用了这个链接帮助在 Android 应用程序中使用你自己的 SQLite 数据库,但不得不稍微改变它。

  1. 如果你有很多包,你应该把主包名放在这里:

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

  2. 我更改了将数据库从本地文件夹复制到模拟器文件夹的方法! 当该文件夹不存在时,它有一些问题。 所以首先,它应该检查路径,如果它不存在,它应该创建文件夹。

  3. 在上面的代码中, copyDatabase方法从来没有所谓当数据库不存在与checkDataBase造成异常的方法。 所以我稍微改变了代码。

  4. 如果您的数据库没有文件扩展名,请不要将文件名与一个一起使用。

它对我有用,我希望它对你也有用

    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.

}

我修改了课程和问题的答案,并编写了一个允许通过 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;
    }
}

使用一个类。

在活动类中,声明变量。

private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;

在 onCreate 方法中,编写如下代码。

mDBHelper = new DatabaseHelper(this);

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

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

如果将数据库文件添加到文件夹 res/raw 中,则使用类的以下修改。

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

将数据库传送到 apk 中,然后将其复制到/data/data/...将使数据库的大小加倍(apk 中为 1, data/data/... ),并将增加 apk 大小(课程)。 所以你的数据库不应该太大。

如果所需的数据不是太大(我不知道的限制,将取决于很多事情),您还可以从网站/webapp 下载数据(XML、JSON 等)。 接收后,使用接收到的数据执行 SQL 语句,创建您的表并插入数据。

如果您的移动应用程序包含大量数据,那么稍后使用更准确的数据或更改来更新已安装应用程序中的数据可能会更容易。

Android 已经提供了一种版本感知的数据库管理方法。 这种方法已在 Android 应用程序的 BARACUS 框架中得到利用。

它使您能够在应用程序的整个版本生命周期中管理数据库,能够将 sqlite 数据库从任何先前版本更新到当前版本。

此外,它还允许您运行 SQLite 的热备份和热恢复。

我不是 100% 确定,但特定设备的热恢复可能使您能够在您的应用程序中发送准备好的数据库。 但我不确定可能特定于某些设备、供应商或设备代的数据库二进制格式。

由于内容是Apache License 2,请随意重用代码的任何部分,可以在github上找到

编辑 :

如果您只想传送数据,您可以考虑在应用程序第一次启动时实例化和持久化 POJO。 BARACUS 获得了对此的内置支持(用于配置信息的内置键值存储,例如“APP_FIRST_RUN”加上一个 after-context-bootstrap 钩子,以便在上下文上运行启动后操作)。 这使您能够拥有与应用程序一起提供的紧耦合数据; 在大多数情况下,这适合我的用例。

我写了一个来简化这个过程。

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

它将从assets/databases/myDb.db文件创建一个数据库。 此外,您将获得所有这些功能:

  • 从文件加载数据库
  • 同步访问数据库
  • 通过 requery 使用sqlite-android ,Android 特定发行版 SQLite 的最新版本。

github克隆它。

我正在使用 ORMLite,下面的代码对我有用

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

    }
}

请注意,该代码从资产中的 zip 文件中提取数据库文件

如果您使用ROOM ,官方文档中已经有一个非常直接的路径https://developer.android.com/training/data-storage/room/prepopulate 以下是从资产文件重新填充数据库的操作方法:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromAsset("database/myapp.db")
    .build()

或从文件:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromFile(File("mypath"))
    .build()

如果您不使用 Room,我强烈建议您使用 😁

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM