在我的Android项目中,有许多数据库在不同的线程中读写。为了提高阅读速度,我使用了SQLiteDatabase.enableWriteAheadLogging,有时还有异常
java.lang.IllegalStateException:Write Ahead Logging(WAL)模式不能 在正在进行的事务中启用或禁用。 完成所有事务并释放所有活动数据库连接 第一
得到了。但是在检查代码之后,所有的写操作都是互斥的。 所以我写了一个示例代码来重现问题。通话后
new DbTest().doTest();
异常将在3-5分钟内发生。这是测试代码:
private class DbTest extends Handler{
Thread readThrd = null;
Thread writeThrd = null;
@Override
public void handleMessage(Message msg){
if (msg.what == 100)
readThrd = null;
else if (msg.what == 200)
writeThrd = null;
if (readThrd == null && writeThrd == null){
doTest();
}
}
private DatabaseHelper mDbHelper;
public void doTest(){
mDbHelper = new DatabaseHelper(MyActivity.this);
SQLiteDatabase db = mDbHelper.getWritableDatabase();
db.execSQL("delete from " + TABLE_NAME + " where 1");
//new ReadThread(mDbHelper).start();
readThrd = new ReadThread(mDbHelper, TABLE_NAME, this);
writeThrd = new WriteThread(mDbHelper, TABLE_NAME, this);
writeThrd.start();
readThrd.start();
}
}
private class ReadThread extends Thread{
DatabaseHelper mDbHelper = null;
private String tableName;
private Handler hdl;
public ReadThread(DatabaseHelper db, String tblName, Handler hdl){
mDbHelper = db;
tableName = tblName;
this.hdl = hdl;
}
public void run(){
Log.e("DBG", "Read Start");
int count = 0;
while(count++ < 300) {
//Log.e("DBG", "read open:\t" + count);
try {
mDbHelper.addReadingCount();
SQLiteDatabase db = mDbHelper.getReadableDatabase();
for (int i = 0; i < 100; ++i) {
String sql = "select count(*) from " + tableName;
//String sql = "select * from " + tableName;
Cursor c = db.rawQuery(sql, null);
int len = 1;
if (null != c && c.moveToFirst()) {
len = c.getInt(0);
/*
do {
len = c.getInt(0);
if (len % 10 == 0 ) {
//Log.e("DBG", "read:\t" + len);
break;
}
} while (c.moveToNext() && len < 10);
*/
}
if (null != c) {
c.close();
}
}
}finally{
mDbHelper.removeReadingCount();
}
}
try {
Thread.sleep(100);
}catch (InterruptedException e){
}
//Log.e("DBG", "read close:\t" + count);
//db.close();
Log.e("DBG", "Read End");
hdl.sendEmptyMessageDelayed(100, 1000);
}
}
private class WriteThread extends Thread{
DatabaseHelper mDbHelper = null;
String tableName;
Handler hdl;
public WriteThread(DatabaseHelper db, String tableName, Handler hdl){
mDbHelper = db;
this.tableName = tableName;
this.hdl = hdl;
}
public void run(){
Log.e("DBG", "Write Start");
int count = 0;
while(count++ < 101) {
synchronized (write) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
if (db.inTransaction()){
db.close();
Log.d("DBG", "Close by transaction");
db = mDbHelper.getWritableDatabase();
}
/*
while(db.inTransaction()){
Log.e("DBG", "Wait Transaction");
try {
Thread.sleep(50);
}catch (InterruptedException e){
}
}
*/
db.enableWriteAheadLogging();
//Log.e("DBG", "Write open:\t" + count + ", " + b);
try {
db.beginTransactionNonExclusive();
long now = new Date().getTime();
for (int i = 0; i < 1000; ++i) {
String sql = "insert into " + tableName + " (NAME, INFO) values ('%1$s', '%2$s')";
String name = "" + now + "-" + i;
sql = String.format(sql, name, "info");
db.execSQL(sql);
}
//Log.e("DBG", "Write close:\t" + count);
db.setTransactionSuccessful();
}finally {
db.endTransaction();
}
//db.close();
mDbHelper.closeDb(db);
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
SQLiteDatabase db = mDbHelper.getWritableDatabase();
db.close();
Log.e("DBG", "Write End");
hdl.sendEmptyMessageDelayed(200, 1000);
}
}
private static final String DATABASE_NAME = "info.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME= "Info";
private static final String TABLE_NAME2 = "Infoa";
private static class DatabaseHelper extends SQLiteOpenHelper {
volatile Vector<Integer> readingCount = new Vector<Integer>();
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建用于存储数据的表
db.execSQL("Create table " + TABLE_NAME + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, INFO TEXT);");
db.execSQL("Create table " + TABLE_NAME2 + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, INFO TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public boolean isReading(){
return (readingCount.size() != 0);
}
public void addReadingCount(){
int sz = readingCount.size();
readingCount.add(1);
}
public void removeReadingCount(){
int sz = readingCount.size();
if (readingCount.size() > 0)
readingCount.remove(0);
}
public void closeDb(SQLiteDatabase db){
/*
synchronized (readingCount) {
if (readingCount.size() > 0)
return;
else
db.close();
}*/
synchronized (readingCount) {
while(readingCount.size() > 0) {
try {
//Log.e("DBG", "WAIT START");
readingCount.wait(30);
//Log.e("DBG", "WAIT STOP");
}catch (InterruptedException e) {
}
}
//Log.e("DBG", "Closed" + readingCount.size());
db.close();
}
}
}
}
在上面的代码中,所有写操作都被synchronized(write)锁定,但为什么是异常
java.lang.IllegalStateException:Write Ahead Logging(WAL)模式不能 在正在进行的事务中启用或禁用。 完成所有事务并释放所有活动数据库连接 第一
还会发生吗? 如果删除Thread.Sleep(),则不会发生异常,这很奇怪。有人帮忙吗?我也试图不关闭数据库,但仍存在相同的异常。
答案 0 :(得分:3)
WAL设置是永久性的,因此您只需启用一次。
启用它的正确方法是在setWriteAheadLoggingEnabled()中调用onConfigure() callback。