【问题标题】:can't open sqlite file in assets folder to copy to device无法打开资产文件夹中的 sqlite 文件以复制到设备
【发布时间】:2014-11-18 05:17:22
【问题描述】:

这是我在Android 上的第一个申请。我看到了一些关于这个问题的问题,但没有一个问题适合我。

我在 assets 文件夹中有一个名为 fmdb.sqlite 的数据库。我使用了此链接中的代码示例: http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/

当我到达应该获取资产文件夹中 sqlite 文件的流的行时(copyDataBase 方法),我得到一个异常“java.io.FileNotFoundException : fmdb.sqlite”。

谁能告诉我我做错了什么?这是我的课。

public class DatabaseOpener extends SQLiteOpenHelper {

// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;

//The Android's default system path of your application database.
public static String DB_PATH;

public static String DB_NAME = "fmdb.sqlite";

private SQLiteDatabase myDataBase;

private final Context myContext;



public DatabaseOpener(Context context) throws IOException {
    super(context, DB_NAME, null, DATABASE_VERSION);
    myContext = context; 
}

/**
 * Check if the database already exists 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;
       checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

       }catch(SQLiteException e){

       //database does't exist yet.

       }

       if(checkDB != null){

       checkDB.close();

   }

   return checkDB != null ? true : false;
   }

   /**
    * Creates a empty database on the system and rewrites it with your own database.
    * */
  public void createDataBase() throws IOException{

      boolean dbExist = checkDataBase();

      if(dbExist){
          //do nothing - database already exist
          }else{

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

          try {

          copyDataBase();

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

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

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

 }

 public SQLiteDatabase openDataBase() throws SQLException{

    //Open the database
    String myPath = DB_PATH;// + DB_NAME;
    myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

    return myDataBase;
}

 @Override
public synchronized void close() {

    if(myDataBase != null)
    myDataBase.close();

    super.close();

}



@Override
public void onCreate(SQLiteDatabase db) {
    // TODO Auto-generated method stub

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // TODO Auto-generated method stub

}

}

编辑 2 这是 LogCat。我卸载并重新运行该程序。现在是关于打开db文件进行编写,非常感谢您的帮助!

09-29 17:59:19.540: W/SQLiteAssetHelper(27063): copying database from assets... 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): Couldn't open fmdb.sqlite for writing (will try read-only): 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): com.readystatesoftware.sqliteasset.SQLiteAssetHelper$SQLiteAssetException: Missing databases/fmdb.sqlite file (or .zip, .gz archive) in assets, or target folder not writable 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.content.res.AssetManager.openAsset(Native Method) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.content.res.AssetManager.open(AssetManager.java:315) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.content.res.AssetManager.open(AssetManager.java:289) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.copyDatabaseFromAssets(SQLiteAssetHelper.java:436) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.createOrOpenDatabase(SQLiteAssetHelper.java:400) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getWritableDatabase(SQLiteAssetHelper.java:176) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getReadableDatabase(SQLiteAssetHelper.java:254) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.friendlymeter.dal.MyDatabase.getSample(MyDatabase.java:27) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.friendlymeter.app.MainActivity$2.onClick(MainActivity.java:105) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.view.View.performClick(View.java:4204) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.view.View$PerformClick.run(View.java:17355) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.os.Handler.handleCallback(Handler.java:725) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.os.Handler.dispatchMessage(Handler.java:92) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.os.Looper.loop(Looper.java:137) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.app.ActivityThread.main(ActivityThread.java:5041) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at java.lang.reflect.Method.invokeNative(Native Method) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at java.lang.reflect.Method.invoke(Method.java:511) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at dalvik.system.NativeStart.main(Native Method) 09-29 17:59:19.709: E/SQLiteLog(27063): (14) cannot open file at line 30176 of [00bb9c9ce4] 09-29 17:59:19.709: E/SQLiteLog(27063): (14) os_unix.c:30176: (2) open(/data/data/com.friendlymeter.app/databases/fmdb.sqlite) - 09-29 17:59:19.909: D/dalvikvm(27063): GC_CONCURRENT freed 130K, 9% free 2652K/2904K, paused 9ms+15ms, total 171ms 09-29 17:59:20.019: E/SQLiteDatabase(27063): Failed to open database '/data/data/com.friendlymeter.app/databases/fmdb.sqlite'. 09-29 17:59:20.019: E/SQLiteDatabase(27063): android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getReadableDatabase(SQLiteAssetHelper.java:264) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.friendlymeter.dal.MyDatabase.getSample(MyDatabase.java:27) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.friendlymeter.app.MainActivity$2.onClick(MainActivity.java:105) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.view.View.performClick(View.java:4204) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.view.View$PerformClick.run(View.java:17355) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.os.Handler.handleCallback(Handler.java:725) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.os.Handler.dispatchMessage(Handler.java:92) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.os.Looper.loop(Looper.java:137) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.app.ActivityThread.main(ActivityThread.java:5041) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at java.lang.reflect.Method.invokeNative(Native Method) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at java.lang.reflect.Method.invoke(Method.java:511) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

  • use SQLiteAssetHelper 将数据库打包为资产。
  • DB_PATH 始终为空。这段代码应该如何工作?
  • @greenapps 在初始化 DB_PATH 的类中有一个静态部分(未包含在代码中,但它在那里......)

标签: android database sqlite sqliteopenhelper android-assets


【解决方案1】:

我 100% 同意 CommonsWare 的评论。最简单的方法是使用SQLiteAssetHelper。我会将其发布为答案,但这是从上面的链接粘贴的 100% 副本。要使用这个库,您只需要知道这些。

SQLiteAssetHelper 旨在作为框架的 SQLiteOpenHelper 的替代品。请熟悉该类的行为和生命周期。

像通常的 SQLiteOpenHelper 一样扩展 SQLiteAssetHelper,为构造函数提供数据库名称和版本号:

public class MyDatabase extends SQLiteAssetHelper {

    private static final String DATABASE_NAME = "northwind.db";
    private static final int DATABASE_VERSION = 1;

    public MyDatabase(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
}

SQLiteAssetHelper 依赖于资产文件和文件夹命名约定。您的 assets 文件夹将位于您的项目根目录下,或者如果您使用默认的 gradle 项目结构,则位于 src/main 下。至少,您必须提供以下内容:

资产中的数据库文件夹 数据库文件夹中的 SQLite 数据库,其文件名与您在代码中提供的数据库名称匹配(包括文件扩展名,如果有) 对于上面的示例,该项目将包含以下内容:

资产/数据库/northwind.db 该库的早期版本要求将数据库资产压缩到 ZIP 存档中。这不再是要求,但仍受支持。仍以 Gingerbread (API 10) 或更低版本为目标的应用程序应继续提供压缩存档,以确保在打包过程中不会损坏大型数据库文件。还支持对 Linux 更友好的 GZIP 格式。使用上述示例的命名约定如下:

ZIP:assets/databases/northwind.db.zip(单个 SQLite 数据库文件必须是存档中的唯一文件) GZIP:资产/数据库/northwind.db.gz 数据库将从资产中提取并复制到应用程序的私有数据目录中。如果您希望将数据库文件存储在其他地方(例如外部存储),则可以使用备用构造函数来指定存储路径。当您的应用程序需要访问数据库时,您必须确保此路径可用且可写。

super(context, DATABASE_NAME, context.getExternalFilesDir(null).getAbsolutePath(), null, DATABASE_VERSION);

第一次调用 getReadableDatabase() 或 getWritableDatabase() 时,该数据库可供使用。

如果您没有提供适当命名的文件,该类将抛出 SQLiteAssetHelperException。

此实现不支持 SQLiteOpenHelper 方法 onConfigure、onCreate 和 onDowngrade,并且已声明为 final。

编辑

请求帮助,现在给予。首先,您必须assets 文件夹中放置一个databases 文件夹。它必须命名为databases拼写准确且区分大小写。那是图书馆寻找您的数据库的地方。接下来,您必须sqliteassethelper-2.0.1.jar 放入您的libs 文件夹中。现在只需编写代码。

这里是一些示例代码。

public class MyDatabase extends SQLiteAssetHelper {

    private static String DATABASE_NAME = "your_database_name.sqlite"; // must have file extension
    private static int DATABASE_VERSION = 1;

    public static final String SAMPLE_TABLE = "your_table_name";

    public static final String SAMPLE_COLUMN= "your_column_name";

    public MyDatabase(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * Returns a sample string.
     * @return sample - a sample string from your database table
     */
    public String getSample() {
        SQLiteDatabase database = this.getReadableDatabase();
        String[] columnsArray = { SAMPLE_COLUMN };
        Cursor cursor = database.query(true, SAMPLE_TABLE, columnsArray, null, null, null, null, null, null);
        if (cursor != null) {
            cursor.moveToFirst();
        }

        String sample = cursor.getString(cursor.getColumnIndex(SAMPLE_COLUMN));

        cursor.close();
        database.close();

        return sample;
    }
}

然后你像这样在你的应用程序中调用它

public void databaseTest() {
    MyDatabase db = new MyDatabase(YourActivity.this);
    String sample = db.getSample();
    Log.v("SAMPLE", sample);
    db.close();
}

如果你还需要什么,请告诉我

【讨论】:

  • 我创建了一个扩展 SQLiteAssetHelper 的类,并在 data/data/.../databases 文件夹中添加了一个创建数据库的方法。这里是: public void testCreation() { SQLiteDatabase db = getReadableDatabase();但是当被调用时 - 我得到一个异常:android.database.sqlite.SQLiteCantOpenDatabaseException:未知错误(代码 14):无法打开数据库我试图将数据库文件直接放在 assets 文件夹中,然后放在 assets/databases 文件夹中 - 仍然没有运气。帮助?...
  • 我复制了这个并放了相关的表名和列,现在得到一个不同的错误(欢呼?):'android.database.sqlite.SQLiteCantOpenDatabaseException:未知错误(代码14):无法打开数据库'我现在正在谷歌搜索 - 我的数据库正在 sqlite 浏览器中打开,所以它没有损坏。
  • @amazingwolf 三件事。一,确保您的数据库位于数据库文件夹中。二,从您的模拟器和/或设备上完全卸载您的应用程序。然后再次运行您的程序。三、用你的logcat消息更新你的代码,这样我就可以看到新的代码
  • @amazingwolf 没问题,很高兴它的工作,当你有机会时接受答案
  • 实际上现在我得到了一个不同的错误,我在问题中发布了 logcat 作为编辑...我是新手,非常感谢您的耐心。如果您能提供帮助,那就太好了,我正在谷歌搜索,我发现的只是关于缺少 db 文件扩展名的引用,这在我的代码中不是这种情况。
【解决方案2】:

好的 - 我终于让它与我最初发布的代码一起工作。该解决方案不需要使用 SQLiteAssetHelper(尽管它确实减少了必须编写的代码量 - 感谢 @javascript 的大力帮助)。我拿了一个旧的 sqlite db 并用它运行应用程序 - 读取它时它没有抛出异常。尽管我的原始文件已在所有 sqlite 管理工具中打开(因此没有损坏),但我决定创建一个新的数据库文件。

我用来测试代码的旧数据库和应用程序数据库之间的区别是(除了模式)文件扩展名。旧的数据库扩展名是“.db”,而应用程序数据库是“.sqlite”。所以我创建了一个带有“.db”扩展名的新数据库,在其中创建了所有表 - 瞧 - 应用程序成功地将文件从 assets 文件夹复制到模拟器中的数据库文件夹。

希望这对某人有所帮助,这真的很令人沮丧:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 2012-05-31
    • 1970-01-01
    • 2017-09-17
    • 2012-01-14
    • 2011-02-25
    • 2010-12-28
    相关资源
    最近更新 更多