【问题标题】:Android: copy database from asset folder, but only get an empty fileAndroid:从资产文件夹复制数据库,但只得到一个空文件
【发布时间】:2011-09-01 12:02:50
【问题描述】:

伙计们,我在将数据库从本地资产文件夹复制到 /data/data/package_name/databases 目录时遇到问题。当我使用http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/ 教程来做这件事时,我只能得到一个空文件。

我引用了copyDataBase()方法的部分,没有区别。每次应用程序启动时,它都会创建目录和空数据库。那么有什么方法可以使 copyDataBase() 工作?

非常感谢!!

【问题讨论】:

标签: android sqlite android-assets


【解决方案1】:

为什么不从资产中复制?这样做是完全正常的。但是您不能在 onCreate 中执行此操作,此时已经创建了一个空数据库。你需要事先做。我通常在覆盖 getWriteableDatabase 时这样做,比如

public synchronized SQLiteDatabase getWritableDatabase() {
    SQLiteDatabase db = null;

    if (!doesDatabaseExist()) {
        try {
            copyDatabase();
            db = super.getWritableDatabase();
        } catch(Exception ex) {
            Log.e("Database Log", getDatabasePath() + " failed to copy correctly. " + ex.getLocalizedMessage());
        }
    }
    else {
        db = super.getWritableDatabase();
    }

    return db;
}

【讨论】:

  • 为我工作 (Android 2.3.3)。干净而坚实。
  • 我不得不稍微改变一下:if (!doesDatabaseExist()) { try { SQLiteDatabase db = super.getReadableDatabase(); db.close(); } catch (Exception ignore) {} copyFromAssets(); return super.getReadableDatabase(); } 否则它会因 FileNotFoundException 而崩溃,可能是因为数据库目录不存在。 (Android 2.2)
【解决方案2】:

我不会从assets-文件夹中复制任何数据库。如果您需要在数据库中添加一些标准条目,您可以使用INSERTs 在您的onCreate()- 中添加它们方法。


更新:由于这是错误的(这有点正确),我无法删除它,所以这里有一点更新。

我会说这取决于您要添加到数据库中的标准条目的数量。如果只是一两个,那么运送一个打包好的数据库可能不值得。

总之,有些应用带有相当大的数据库(例如,食谱集合)。您显然不能将所有这些都添加到代码中。

  • 对于小型测试条目,我仍然更愿意将它们简单地添加到 onCreate()
  • 对于更大的数据库,您应该预先填充它们并将它们与您的应用程序一起提供。

为了使后者正常工作,您需要将数据库文件从 assets/ 复制到您的应用文件夹。有一个不错的库可以为您处理:android-sqlite-asset-helper

【讨论】:

  • 谢谢!这只是2小时的工作。 :)
  • 为什么不从资产中复制一个 db 文件?在我的例子中,它是一个包含 19 个表和 35k 行插入语句的 R/O 数据库。我看不出我为什么要在设备上初始化这样的数据库。有什么我想念的吗?
  • 有什么方法不需要复制到数据文件夹吗?我们可以直接从 assets 文件夹中读取吗?
【解决方案3】:

我不知道它是否仍然有用,但这里是其他人到这里看遮阳篷的解决方案。您使用的代码适用于大多数手机,一些旧手机与 getReadableDatabase() 函数有不同的行为。因此,您的问题不在 copyDataBase 函数中,而是在 createDataBase 函数中。

在 createDataBase() 中有以下检查;

this.getReadableDatabase();

这会检查是否已经存在具有所提供名称的数据库,如果没有,则会创建一个空数据库,以便可以用 assets 文件夹中的数据库覆盖它。在较新的设备上,这可以完美地工作,但在某些设备上这不起作用。主要是旧设备。我不知道确切原因,但似乎 getReadableDatabase() 函数不仅获取数据库而且还打开它。如果您随后从 assets 文件夹中复制数据库,它仍然具有指向空数据库的指针,您将收到 table does not exist 错误。

因此,为了使其适用于所有设备,您应该将其修改为以下几行:

SQLiteDatabase db = this.getReadableDatabase();
if (db.isOpen()){
    db.close();
}

即使在检查中打开了数据库,之后也关闭了,不会再给你添麻烦了。

【讨论】:

    【解决方案4】:

    上面右边的例子对我有用:

    db = super.getWritableDatabase();
    db.close;
    copyDatabase();
    

    否则会出现 IO 错误;

    【讨论】:

      【解决方案5】:

      这是我使用的简单方便的代码:

      public class DataBaseImportHelper {
          private DataBaseImportHelper() {}; // Avoid instantiation
      
      /**
       * Creates a empty database on the system and rewrites it with your own database.
       */
      public static boolean importDataBase(Context context) {
          InputStream myInput = null;
          OutputStream myOutput = null;
          try {
              // Open local db from assets as the input stream
              myInput = context.getAssets().open(DATABASE_NAME);
              createEmptyDatabase(context); // See this method below
              // Open the empty db as the output stream
              myOutput = new FileOutputStream(getDatabaseFile(context));
      
              // 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();
              myInput.close();
              myOutput.close();
              return true;
          } catch (IOException e) {
              e.printStackTrace();
          }
          return false;
      }
      
      /**
       * Check if the database already exists.
       * @return true if it exists, false if it doesn't
       */
      public static boolean isDatabaseExists(Context context) {
          return getDatabaseFile(context).exists();
      }
      
      private static File getDatabaseFile(Context context) {
          return context.getDatabasePath(DatabaseHelper.DATABASE_NAME);
      }
      
      /**
       * Create an empty database into the default application database
       * folder.So we are gonna be able to overwrite that database with our database
       */
      private static void createEmptyDatabase(Context context) {
                  // use anonimous helper to create empty database
          new SQLiteOpenHelper(context, DatabaseHelper.DATABASE_NAME, null, 1) {
                          // Methods are empty. We don`t need to override them
              @Override
              public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
              }
      
              @Override
              public void onCreate(SQLiteDatabase db) {
              }
          }.getReadableDatabase().close();
      }
      

      }

      并在代码中使用:

      if(!DataBaseImportHelper.isDatabaseExists(this)){
          if (!DataBaseImportHelper.importDataBase(this)){
                  throw new IllegalStateException("Database doesn`t exist and hasn`t been copied!");
          }
      }
      

      【讨论】:

      • 关键是调用createEmptyDatabase()。没有这个,对“new FileOutputStream()”的调用就会失败,即使它应该在文件不存在时创建它。
      猜你喜欢
      • 1970-01-01
      • 2013-12-11
      • 2014-01-02
      • 1970-01-01
      • 2013-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多