从/ assets复制预先填充的数据库

时间:2012-06-06 21:48:07

标签: android

我正在使用this article中的代码将预先填充的数据库复制到新创建的数据库中,从而覆盖它。

覆盖成功。我能够在正确的路径data / data / my.package / databases / myDatabase中看到正确复制的数据库。但这只是在我离开getWritableDatabse()之前查看数据库。

如果我离开getWritableDatabse(),db就会失效:它仍然保持正确的大小但是它只有android_metadata而不是我的表,然后全部消失了。

我没有设法访问Eclipse的Android源代码(遵循所有说明,但仍然无效)但使用GrepCode我设法将问题缩小到第173-176行方法getWritableDatabase()

173 db.setVersion(mNewVersion);
174 db.setTransactionSuccessful();
175 } finally {
176   db.endTransaction();

任何人都知道为什么这段代码可能会重写我的数据库并清空它?

这是相关的堆栈:

Thread [<1> main] (Suspended)   
StournamentDbAdapter$DatabaseHelper(SQLiteOpenHelper).getWritableDatabase() line: 180   
StournamentDbAdapter.open() line: 192   
ActivityTournamentsList.onCreate(Bundle) line: 57   
ActivityTournamentsList(Activity).performCreate(Bundle) line: 4465  
Instrumentation.callActivityOnCreate(Activity, Bundle) line: 1049   
ActivityThread.performLaunchActivity(ActivityThread$ActivityClientRecord, Intent) line: 1920    
ActivityThread.handleLaunchActivity(ActivityThread$ActivityClientRecord, Intent) line: 1981 
ActivityThread.access$600(ActivityThread, ActivityThread$ActivityClientRecord, Intent) line: 123    
ActivityThread$H.handleMessage(Message) line: 1147  
ActivityThread$H(Handler).dispatchMessage(Message) line: 99 
Looper.loop() line: 137 
ActivityThread.main(String[]) line: 4424    
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]  
Method.invoke(Object, Object...) line: 511  
ZygoteInit$MethodAndArgsCaller.run() line: 784  
ZygoteInit.main(String[]) line: 551 
NativeStart.main(String[]) line: not available [native method]  

我首先使用调用mDb = mDbHelper.getWritableDatabase();的{​​{1}}来实现db的创建。在DatabaseHelper.onCreate()中,我只调用DatabaseHelper.onCreate(),如下所示:

copyDatabase();

所有这一切都很好,数据库被正确复制。只有当我爬出堆栈并返回private void copyDatabase() throws IOException { //Open your local db as the input stream InputStream myInput = sContext.getAssets().open(StournamentConstants.Database.DATABASE_NAME); // Path to the just created empty db String outFileName = StournamentConstants.Database.DB_PATH + StournamentConstants.Database.DATABASE_NAME; //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(); } 时出现问题:db的大小保持不变,但除了àndroid_metadata`之外它会丢失所有表。


中间结论

我在代码中挖了一些(上面提到的GrepCode网站非常有用),因为我想深究这一点。进入getWritableDatabase() line: 180后,在第165行,它将调用getWritableDatabase(),我使用onCreate()替换数据库。问题是之前,copyDatabase(); getWritableDatabase() line 162被调用。为什么这是一个问题?我将通过展示事件的顺序来解释:

  1. 致电db.beginTransaction();
  2. getWritableDatabase()调用getWritableDatabase()因此创建了数据库
  3. db = SQLiteDatabase.create(null);来电getWritableDatabase()
  4. db.beginTransaction();来电getWritableDatabase()
  5. inside onCreate()我通过调用onCreate()来替换数据库,copyDatabase();将db从资产复制到db位置。不知道为什么已经创建的db在那一刻不会崩溃。也许是因为它没有锁定?
  6. 第176行getWritableDatabase()调用db.endTransaction();这是至关重要的部分:很好,很好,我将数据库复制到最初创建的数据库中,但是当调用db.endTransaction();时,系统将所有更改提交给我新复制的数据库。但请记住:我没有做任何更改,我没有创建表 - 我只是将数据库复制到正确的位置。因此,commit提交NOTHING,从而用NOTHING重写db。 为此我得到一个空数据库
  7. 我有邪恶的想法:

    1. onCreate()
    2. 中复制数据库
    3. 打开新复制的数据库,并用它替换db getWritableDatabase()次使用(onCreate()getWritableDatabase()获取db参数)。
    4. 类似的东西:

      @Override
      public void onCreate(SQLiteDatabase db) 
      {
          try 
          {               
              copyDatabase();
      
              String dbPath = StournamentConstants.Database.DB_PATH + StournamentConstants.Database.DATABASE_NAME;
              SQLiteDatabase activeDb = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY);              
      
               db = activeDb;
          } 
             catch (IOException e) 
          {
             Log.i(getClass().getName() + ".onCreate ", e.getMessage());
         }   
      }
      

      问题在于我得到android.database.sqlite.SQLiteDatabaseLockedException: database is locked所以我无法获得新复制的数据库。除此之外 - 我不确定这种狡猾的行为背后隐藏着什么险恶的后果。

      所以我会做@Barak建议的并将我的复制移动到构造函数中 - 在系统尝试创建db之前。因此,当最终到达getWritableDatabase()时,数据库已经存在,系统将不会尝试触摸它。我今天学到了很多东西 - 非常高兴。

      结论:如果你能阅读(代码),你就有一个明显的优势:^)

      请告诉您这对我有何用处。


      最终结论

      我解决了这个问题。

      几乎没有差异:

      1. 我不在DatabaseHelper的构造函数中执行此操作。这将需要太多的代码更改。相反,我在open()
      2. private static class DatabaseHelper extends SQLiteOpenHelper内进行此操作
      3. 我不使用getReadableDatabase(),而是使用getWritableDatabase()。原因很简单:getReadableDatabase()总是会调用getWritableDatabase()所以我以为我会保存那个...... {/ li>

        我也试图成为一个明智的人而不是调用getWritableDatabase()(它有一些代码开销)而只是自己创建db文件。我失败了。我认为这是由于许可问题或其他原因造成的。这是我在copyDatabase()

        中尝试的内容
        File f = new File(DB_PATH);
        if (!f.exists()) 
        {
            f.mkdir();
        }
        

        但总是失败。后来我发现OutputStream myOutput = new FileOutputStream(outFileName);实际上应该处理文件创建。

        希望这有助于任何人!

1 个答案:

答案 0 :(得分:1)

嗯,我看到的唯一区别是我没有在onCreate中进行数据库复制/创建,这是空的。我在构造函数中完成所有操作,如下所示:

private static class DatabaseHelper extends SQLiteOpenHelper {
    DatabaseHelper(Context context, String dbname, int dbversion) {
        super(context, dbname, null, dbversion);
            if (checkDataBase(dbname)) {
                openDataBase(dbname);
            } else {
                try {
                    this.getReadableDatabase();
                    copyDataBase(dbname);
                    this.close();
                    openDataBase(dbname);
                } catch (IOException e) {
                     throw new Error("Error copying database");
                }
                Toast.makeText(context,"Initial " + dbname + " database has been created",
                    Toast.LENGTH_LONG).show();
            }
      }
    @Override
    public void onCreate(SQLiteDatabase db) {
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}