我正在使用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
被调用。为什么这是一个问题?我将通过展示事件的顺序来解释:
db.beginTransaction();
getWritableDatabase()
调用getWritableDatabase()
因此创建了数据库db = SQLiteDatabase.create(null);
来电getWritableDatabase()
db.beginTransaction();
来电getWritableDatabase()
onCreate()
我通过调用onCreate()
来替换数据库,copyDatabase();
将db从资产复制到db位置。不知道为什么已经创建的db在那一刻不会崩溃。也许是因为它没有锁定?getWritableDatabase()
调用db.endTransaction();
这是至关重要的部分:很好,很好,我将数据库复制到最初创建的数据库中,但是当调用db.endTransaction();
时,系统将所有更改提交给我新复制的数据库。但请记住:我没有做任何更改,我没有创建表 - 我只是将数据库复制到正确的位置。因此,commit提交NOTHING,从而用NOTHING重写db。 为此我得到一个空数据库。我有邪恶的想法:
onCreate()
getWritableDatabase()
次使用(onCreate()
从getWritableDatabase()
获取db参数)。类似的东西:
@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()
时,数据库已经存在,系统将不会尝试触摸它。我今天学到了很多东西 - 非常高兴。
结论:如果你能阅读(代码),你就有一个明显的优势:^)
请告诉您这对我有何用处。
最终结论
我解决了这个问题。
几乎没有差异:
DatabaseHelper
的构造函数中执行此操作。这将需要太多的代码更改。相反,我在open()
类private static class DatabaseHelper extends SQLiteOpenHelper
内进行此操作
getReadableDatabase()
,而是使用getWritableDatabase()
。原因很简单:getReadableDatabase()
总是会调用getWritableDatabase()
所以我以为我会保存那个...... {/ li>
醇>
我也试图成为一个明智的人而不是调用getWritableDatabase()
(它有一些代码开销)而只是自己创建db文件。我失败了。我认为这是由于许可问题或其他原因造成的。这是我在copyDatabase()
:
File f = new File(DB_PATH);
if (!f.exists())
{
f.mkdir();
}
但总是失败。后来我发现OutputStream myOutput = new FileOutputStream(outFileName);
实际上应该处理文件创建。
希望这有助于任何人!
答案 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) {
}
}