Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/179.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Android中预填充数据库的最快、最有效的方法_Android_Sql_Database_Sqlite - Fatal编程技术网

在Android中预填充数据库的最快、最有效的方法

在Android中预填充数据库的最快、最有效的方法,android,sql,database,sqlite,Android,Sql,Database,Sqlite,如果您想在Android中预填充数据库(SQLite),这并不像人们想象的那么容易 所以我发现这里也经常提到堆栈溢出 但是我不太喜欢这种预先填充数据库的方式,因为您从数据库处理程序获得控制权,然后自己创建文件。我宁愿不接触文件系统,让数据库处理程序自己完成所有事情 因此,我认为可以像往常一样在数据库处理程序的onCreate()中创建数据库,然后从/assets加载一个文件(.sql),其中包含用于填充值的语句: INSERT INTO testTable (name, pet) VALUES

如果您想在Android中预填充数据库(SQLite),这并不像人们想象的那么容易

所以我发现这里也经常提到堆栈溢出

但是我不太喜欢这种预先填充数据库的方式,因为您从数据库处理程序获得控制权,然后自己创建文件。我宁愿不接触文件系统,让数据库处理程序自己完成所有事情

因此,我认为可以像往常一样在数据库处理程序的onCreate()中创建数据库,然后从/assets加载一个文件(.sql),其中包含用于填充值的语句:

INSERT INTO testTable (name, pet) VALUES ('Mike', 'Tiger');
INSERT INTO testTable (name, pet) VALUES ('Tom', 'Cat');
...
但是在处理程序的onCreate()中调用execSQL()实际上并不起作用。似乎/assets文件的大小不能超过1MB,execSQL()只执行第一条语句(Mike-Tiger)


在预填充数据库之前,您会怎么做?

是的,资产可能有大小限制,因此如果大于限制,您可以剪切到更多文件

exesql支持更多的sql语句,这里举个例子:

    BufferedReader br = null;
    try {
        br = new BufferedReader(new InputStreamReader(asManager.open(INIT_FILE)), 1024 * 4);
        String line = null;
        db.beginTransaction();
        while ((line = br.readLine()) != null) {
            db.execSQL(line);
        }
        db.setTransactionSuccessful();
    } catch (IOException e) {
        FLog.e(LOG_TAG, "read database init file error");
    } finally {
        db.endTransaction();
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                FLog.e(LOG_TAG, "buffer reader close error");
            }
        }
    }
上面的示例需要INIT_文件需要每一行都是sql语句

此外,如果您的sql语句文件很大,您可以在android的网站外创建数据库(sqlite支持windows、linux,因此您可以在操作系统中创建数据库,并将数据库文件复制到资产文件夹中,如果很大,您可以压缩它)

当应用程序运行时,您可以从资产中获取数据库文件,直接保存到应用程序的数据库文件夹中(如果对其进行压缩,则可以解压缩到应用程序的数据库文件夹)


希望可以帮助您-:

您的问题表明,您想要最快的方式-但您不喜欢本文中的方式-您不想手动替换DB文件(尽管它实际上可能比用查询填充空DB更快)

我也有同样的想法——我发现,通过SQL语句填充和预填充都是最好的解决方案——但这取决于使用DB的方式

在我的应用程序中,我需要在第一次运行时在DB中有大约2600行(4列)——这是用于自动完成的数据和其他一些东西。它很少会被修改(用户可以添加自定义记录,但大多数情况下-他们不需要这样做),而且非常大。从SQL语句填充它不仅需要更多的时间,而且需要APK中更多的空间(假设我将数据存储在其中,或者可以从internet下载)

这是一个非常简单的例子(“大”插入只能发生一次,而且只能在第一次启动时发生),我决定继续复制预填充的DB文件。当然,这可能不是最好的方法,但它更快。我希望我的用户能够尽可能快地使用该应用程序,并将速度视为优先事项——他们真的很喜欢它。相反,我怀疑当应用程序速度变慢时,他们会高兴,因为我认为更慢更好的解决方案实际上更好

如果不是2600行,我的表最初会有50行,我会使用SQL语句,因为速度和大小的差异不会太大


你必须决定哪种解决方案更适合你的情况。如果您预见到使用“预填充db”选项可能会出现任何问题,请不要使用它。如果您不确定这些问题,请询问,提供有关如何使用(并最终升级)DB内容的更多详细信息。如果您不确定哪种解决方案会更快,请对其进行基准测试。不要害怕这种复制文件的方法——如果使用得当,它可以非常有效。

我建议如下:

  • 将所有
    INSERT
    逻辑包装到事务中(
    BEGIN…COMMIT
    ,或通过…API)
  • 如前所述,利用绑定API和回收对象
  • 在大容量插入完成之前,不要创建任何索引

  • 另外看一看

    我写了一个类似于前面答案的DbUtils类。它是ORM工具的一部分,在上提供。不同之处在于,它将尝试使用一个简单的正则表达式(而不仅仅是行尾)查找语句边界。如果必须依赖SQL文件,我怀疑是否有更快的方法

    但是,如果您可以以另一种格式提供数据,那么它应该比使用SQL脚本快得多。诀窍是使用一个新的方法。对于每个数据行,将解析后的值绑定到语句并执行该语句。当然,您需要在事务内部执行此操作。我建议使用一种简单的分隔符分隔文件格式(例如CSV),因为它的解析速度比XML或JSON快


    我们为绿岛做了一些。对于我们的测试数据,我们的插入速率约为每秒5000行。出于某种原因,.

    我使用了这种方法。首先创建您的sqlite数据库,有几个程序可以使用,我喜欢。然后将数据库文件复制到资产文件夹中。然后可以在SQLiteOpenHelper的构造函数中使用此代码

    final String outFileName = DB_PATH + NAME;
            
            if(! new File(outFileName).exists()){
                this.getWritableDatabase().close();
                //Open your local db as the input stream
                final InputStream myInput = ctx.getAssets().open(NAME, Context.MODE_PRIVATE);
         
                //Open the empty db as the output stream
                final OutputStream myOutput = new FileOutputStream(outFileName);
                //final FileOutputStream myOutput = context.openFileOutput(outFileName, Context.MODE_PRIVATE);
                
                //transfer bytes from the inputfile to the outputfile
                final byte[] buffer = new byte[1024];
                int length;
                while ((length = myInput.read(buffer))>0){
                    myOutput.write(buffer, 0, length);
                }
         
                //Close the streams
                myOutput.flush();
                ((FileOutputStream) myOutput).getFD().sync();
                myOutput.close();
                myInput.close();
            }
            } catch (final Exception e) {
                // TODO: handle exception
            }
    
    DB_路径类似于/data/data/com.mypackage.myapp/databases/

    名称是您选择的任何数据库名称“mydatabase.db”


    我知道这段代码有很多改进,但它工作得很好,速度也很快。所以我就不管了。这样在onCreate()方法中可能会更好。此外,每次检查文件是否存在可能不是最好的方法。不管怎么说,就像我说的那样,它工作迅速可靠。

    你可以吃蛋糕,也可以吃蛋糕。这里有一个解决方案,它既可以尊重db适配器的使用,也可以对预填充的数据库使用一个简单(且快得多)的复制过程

    我正在使用一个基于Google示例之一的db适配器。它包括一个内部类dbHelper(),该类扩展了Android的SQLiteOpenHelper()类。诀窍是覆盖它的onCrea
        @Override
        public void onCreate(SQLiteDatabase db) {
            mNeedToCopyDb = true;
        }
    
            private Boolean mNeedToCopyDb = false;
    
        /**
     * Open the database using the adapter. If it cannot be opened, try to
     * create a new instance of the database. If it cannot be created,
     * throw an exception to signal the failure.
     * 
     * @return this (self reference, allowing this to be chained in an
     *         initialization call)
     * @throws SQLException if the database could neither be opened nor created
     */
    public MyDbAdapter open() throws SQLException {
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getReadableDatabase();
    
        if (mDbHelper.mNeedToCopyDb == true){
            mDbHelper.close();
            try {
                copyDatabase();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mDbHelper = new DatabaseHelper(mCtx);
                mDb = mDbHelper.getReadableDatabase();
            }
        }
        return this;
    }
    
    dbInputStream = mCtx.getResources().openRawResource(R.raw.mydatabase);