关于android:使用数据库发送应用程序

Ship an application with a database

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

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

  • 在应用程序中包含SQL命令,并让它创建数据库,并在第一次使用时插入数据?

  • 我看到的缺点是:

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

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

  • 有什么建议吗?如有任何问题,请提供相关文件。


    我刚刚在一篇题为"在Android应用程序中使用您自己的sqlite数据库"的文章中找到了一种在ReignDesign blog中实现这一点的方法。基本上,您可以预先创建数据库,将其放在APK中的资产目录中,并在第一次使用时复制到/data/data/YOUR_PACKAGE/databases/目录。


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

    一种方法是在外部创建数据库,然后将其放在项目的assets文件夹中,然后从中复制整个数据库。如果数据库中有很多表和其他组件,这就快得多。升级是通过更改res/values/strings.xml文件中的数据库版本号触发的。然后,通过在外部创建一个新数据库、用新数据库替换资产文件夹中的旧数据库、用另一个名称将旧数据库保存在内部存储中、将新数据库从资产文件夹复制到内部存储、从旧数据库(已重命名)传输所有数据来完成升级。之前)进入新数据库,最后删除旧数据库。您可以通过使用sqlite manager firefox插件来执行您的创建SQL语句来创建一个数据库。

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

    下面的示例演示如何使用这两种方法。

    下面是一个示例create_database.sql文件。它将被放置在内部方法的项目的资产文件夹中,或复制到sqlite manager的"执行SQL"中,为外部方法创建数据库。(注:注意android所需的表注释。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    --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";

    这是一个更新示例database.sql文件。它将被放置在内部方法的项目的资产文件夹中,或复制到sqlite manager的"执行SQL"中,为外部方法创建数据库。(注意:请注意,本示例中包含的SQL解析器将忽略所有三种类型的SQL注释。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    --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文件中的数据库版本号的条目。

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    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数据库。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    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文件的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    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 toFile that is a byte for byte a copy
         * of fromFile. If toFile already existed, then
         * it will be replaced with a copy of fromFile. The name and
         * path of toFile will be that of toFile. Both
         * fromFile and toFile 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 toFile that is a byte for byte a copy
         * of fromFile. If toFile already existed, then
         * it will be replaced with a copy of fromFile. The name and
         * path of toFile will be that of toFile. Both
         * fromFile and toFile 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 toFile that is a byte for byte a copy
         * of fromFile. If toFile already existed, then
         * it will be replaced with a copy of fromFile. The name and
         * path of toFile will be that of toFile. Both
         * fromFile and toFile 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 toFile that is a byte for byte a copy
         * of fromFile. If toFile already existed, then
         * it will be replaced with a copy of fromFile. The name and
         * path of toFile will be that of toFile. Both
         * fromFile and toFile 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库使这个任务非常简单。

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

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

    如文件所述:

  • 将依赖项添加到模块的渐变构建文件:

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

    (也可以选择将数据库压缩到一个zip文件中,如assets/databases/my_database.zip。不需要这样做,因为APK已经压缩为一个整体。)

  • 创建类,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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中导入和使用外部数据库。

    The Android SQLiteAssetHelper library allows you to build your SQLite
    database in your desktop computer, and to import and use it in your
    Android application. Let's create a simple application to demonstrate
    the application of this library.

    Step 1: Create a database quotes.db using your favorite SQLite
    database application (DB Browser for SQLite is a portable cross
    platform freeware, which can be used to create and edit SQLite
    databases). Create a table 'quotes' with a single column 'quote'.
    Insert some random quotes into the table 'quotes'.

    Step 2: The database can be imported into project either directly as
    it is, or as a compressed file. The compressed file is recommended, if
    your database is too large in size. You can create either a ZIP
    compression or a GZ compression.

    The file name of the compressed db file must be quotes.db.zip, if you
    are using ZIP compression or quotes.db.gz, if you are using GZ
    compression.

    Step 3: Create a new application External Database Demo with a
    package name com.javahelps.com.javahelps.externaldatabasedemo.

    Step 4: Open the build.gradle (Module: app) file and add the following
    dependency.

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

    Once you have saved the build.gradle file click on the 'Sync Now'
    link to update the project. You can synchronize the build.gradle, by
    right clicking on the build.gradle file and selecting Synchronize
    build.gradle
    option as well.

    Step 5: Right click on the app folder and create new assets folder.

    Step 6: Create a new folder 'databases' inside the assets folder.

    Step 7: Copy and paste the quotes.db.zip file inside the
    assets/databases folder.

    Step 8: Create a new class DatabaseOpenHelper

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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.

    Step 9: Create a new class DatabaseAccess and enter the code as shown
    below. More details about this class is available at Advanced Android
    Database tutorial.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    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,

    update and delete any rows in the database as usual. For more details,
    follow this link Advanced Android Database.

    All the database related setups are completed and now we need to
    create a ListView to display the quotes.

    Step 10: Add a ListView in your activity_main.xml.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <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>

    Step 11: Find the object of ListView in the onCreate method of MainActivity and feed the quotes which are read
    form the database.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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);
        }
    }

    Step 12: Save all the changes and run the application.

    除了本文之外,您还可以在这里下载SQLiteAssetHelper


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

    先决条件:

  • 您希望随应用程序一起提供的数据库。它应该包含一个名为android_metadata的1x1表,该表的属性locale除了应用程序特有的表之外,还具有值en_US的值。
  • SQLiteOpenHelper子类:

  • SQLiteOpenHelper子类。
  • SQLiteOpenHelper子类中创建一个private方法。此方法包含将数据库内容从"assets"文件夹中的数据库文件复制到应用程序包上下文中创建的数据库的逻辑。
  • 覆盖SQLiteOpenHelperonCreateonUpgradeonOpen方法。
  • 说得够多了。下面是SQLiteOpenHelper子类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    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()即可,如果数据库不存在,它将负责创建数据库,并从"assets"文件夹中的指定文件复制数据库内容。

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


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

    用数据库文件传送应用程序对我来说是个好主意。优点是,如果您的数据集很大,您不需要进行复杂的初始化,有时会花费大量的时间。

    步骤1:准备数据库文件

    准备好数据库文件。它可以是.db文件或.sqlite文件。如果使用.sqlite文件,只需更改文件扩展名。步骤相同。

    在这个例子中,我准备了一个名为testdb.db的文件。它有一个表和一些示例数据,如下所示enter image description here

    步骤2:将文件导入到项目中

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

    enter image description here

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

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

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

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

    enter image description here

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //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
    }

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

    enter image description here

    步骤4:创建数据库打开帮助程序

    SQLiteOpenHelper创建一个子类,包括connect、close、path等,我把它命名为DatabaseOpenHelper

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    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:创建顶级类以与数据库交互

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    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
            }
        }
    }

    第6步:试运行

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

    1
    2
    3
    4
    Database db = new Database(context);
    db.open();
    db.test();
    db.close();

    点击跑步按钮并加油!

    enter image description here


    2017年11月,谷歌发布了Room Persistence Library。

    从文档中:

    The Room persistence library provides an abstraction layer over SQLite
    to allow fluent database access while harnessing the full power of
    SQLite.

    The library helps you create a cache of your app's data on a device
    that's running your app. This cache, which serves as your app's single
    source of truth, allows users to view a consistent copy of key
    information within your app, regardless of whether users have an
    internet connection.

    首次创建或打开数据库时,房间数据库有一个回调。您可以使用创建回调来填充数据库。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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()

    此日志中的代码。


    我终于做到了!!我已经在Android应用程序中使用了这个链接帮助,但是需要对它做一些修改。

  • 如果有许多包,则应在此处输入主包名称:

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

  • 我更改了将数据库从本地文件夹复制到仿真器文件夹的方法!当那个文件夹不存在时出现了一些问题。所以首先,它应该检查路径,如果路径不在那里,它应该创建文件夹。

  • 在前面的代码中,当数据库不存在且checkDataBase方法导致异常时,从未调用copyDatabase方法。所以我修改了一点代码。

  • 如果数据库没有文件扩展名,请不要将文件名与扩展名一起使用。

  • 它对我很好,我希望它对你也有用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
        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.

    }


    根据我所看到的,您应该提供一个已经有表设置和数据的数据库。但是,如果您需要(并且取决于您拥有的应用程序类型),您可以允许"升级数据库选项"。然后,您要做的是下载最新的sqlite版本,获取在线托管的文本文件的最新insert/create语句,执行这些语句,并从旧数据库到新数据库进行数据传输。


    目前无法预创建要随APK一起提供的sqlite数据库。最好将适当的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"(插入到tbl值)可以具体化时,这样做毫无意义。

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


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


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

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


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

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

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

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

    因为这些东西是Apache许可证2,所以可以随意重用代码的任何部分,这些部分可以在Github上找到。

    编辑:

    如果您只想传送数据,可以考虑在应用程序第一次启动时实例化和持久化POJO。Baracus获得了对此的内置支持(配置信息的内置键值存储,例如"app_first_run"加上一个后上下文引导挂钩,以便在上下文上运行启动后操作)。这使您能够将紧密耦合的数据与应用程序一起提供;在大多数情况下,这与我的用例相匹配。


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

    1
    2
    3
    4
    5
    6
    7
    dataBase = new DataBase.Builder(context,"myDb").
    //        setAssetsPath(). // default"databases"
    //        setDatabaseErrorHandler().
    //        setCursorFactory().
    //        setUpgradeCallback()
    //        setVersion(). // default 1
    build();

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

    • 从文件加载数据库
    • 同步访问数据库
    • 使用sqlite android by requery,android发布最新版本的sqlite。

    从Github克隆。


    我修改了这个类和问题的答案,并编写了一个类,允许通过db_版本更新数据库。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    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;
        }
    }

    使用类。

    在Activity类中,声明变量。

    1
    2
    private DatabaseHelper mDBHelper;
    private SQLiteDatabase mDb;

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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文件夹中,请使用类的以下修改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    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;
        }
    }

    Connecting an existing SQLite database to Android Studio


    我使用的是Ormlite,下面的代码对我有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    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文件提取数据库文件