sqlite: Error Code : 5 (SQLITE_BUSY) (database is locked (code 5): , while compiling: PRAGMA journal_mode)
今天遇到了一個很奇怪的問題,登錄完成後,程序會莫名crash, 報了下面的錯誤:
sqlite: Error Code : 5 (SQLITE_BUSY) (database is locked (code 5): , while compiling: PRAGMA journal_mode)
經過分析發現是數據庫被locked,從而導致在調用
mDatabase = mDBHelper.getWritableDatabase();
時發生了crash, 而android上面同一時間向sqlite裏寫數據只允許有一個sqlite connection,所以發生了crash, 不過問題根源還是在數據庫locked, 那麽如何發生locked的呢?
仔細檢查後發現:
private synchronized void copyTableFromAnonymousTable() { SQLiteDatabase db = openDatabase(); try { if (db.isOpen()) { db.execSQL("create table if not exists "+ FLIGHT_LOG_TABLE_NAME + "(id integer primary key autoincrement, " + ""+DBConfig.FLYING_LOG_USER_ID+" integer, "+ ""+DBConfig.FLYING_LOG_USER_NAME+" varchar(100), "+ ""+DBConfig.FLYING_LOG_HOME_LAT+" double, "+ ""+DBConfig.FLYING_LOG_HOME_LON+" double, "+ ""+DBConfig.FLYING_LOG_LAST_LAT+" double, "+ ""+DBConfig.FLYING_LOG_LAST_LON+" double, "+ ""+DBConfig.FLYING_LOG_LOCATION+" varchar(250), "+ ""+DBConfig.FLYING_LOG_CITY+" varchar(50), "+ ""+DBConfig.FLYING_LOG_DRONE_TYPE+" integer, "+ ""+DBConfig.FLYING_LOG_DRONE_TYPE_STR+" varchar(100), "+ ""+DBConfig.FLYING_LOG_DRONE_SSID+" varchar(150), "+ ""+DBConfig.FLYING_LOG_DRONE_SN+" varchar(150), "+ ""+DBConfig.FLYING_LOG_TAKEOFF_TIME+" long, "+ ""+DBConfig.FLYING_LOG_LANDING_TIME+" long, "+ ""+DBConfig.FLYING_LOG_DURATION+" long, "+ ""+DBConfig.FLYING_LOG_TAKEOFF_TIME_STR+" varchar(150), "+ ""+DBConfig.FLYING_LOG_RAW_OFFSET+" long, "+ ""+DBConfig.FLYING_LOG_APP_VERSION+" varchar(100), "+ ""+DBConfig.FLYING_LOG_FC_VERSION+" varchar(100), "+ ""+DBConfig.FLYING_LOG_RC_VERSION+" varchar(100), "+ ""+DBConfig.FLYING_LOG_CAM_VERSION+" varchar(100), "+ ""+DBConfig.FLYING_LOG_MAX_HEIGHT+" double, "+ ""+DBConfig.FLYING_LOG_MAX_SPEED+" double, "+ ""+DBConfig.FLYING_LOG_MAX_DISTANCE +" double, "+ ""+DBConfig.FLYING_LOG_TOTAL_MILEAGE+" double, "+ ""+DBConfig.FLYING_LOG_FLIGHT_STATUS+" double, "+ ""+DBConfig.FLYING_LOG_PLATFORM+" varchar(50), "+ ""+DBConfig.FLYING_LOG_EXTEND_DATA1+" varchar(100), "+ ""+DBConfig.FLYING_LOG_EXTEND_DATA2+" varchar(100), "+ ""+DBConfig.FLYING_LOG_EXTEND_DATA3+" varchar(100), "+ ""+DBConfig.FLYING_LOG_EXTEND_DATA4+" varchar(100)" +")"); db.beginTransaction(); db.execSQL("INSERT INTO "+FLIGHT_LOG_TABLE_NAME+" SELECT * FROM "+FLIGHT_DEFAULT_TABLE_NAME+""); db.setTransactionSuccessful();
db.endTransaction(); } } catch (Exception e) { e.printStackTrace(); } finally { closeDatabase(); }
db.execSQL("INSERT INTO "+FLIGHT_LOG_TABLE_NAME+" SELECT * FROM "+FLIGHT_DEFAULT_TABLE_NAME+"");
執行上述SQL語句時是有問題的,而這個問題被try catch 捕獲掉了,但是:
db.endTransaction();
卻沒有被執行!後來將 db.endTransaction(); 移至finally 代碼塊中發現程序變好了。
以前一直覺得只要記得closeDatabase就好了,卻沒註意到不 end transaction 也會造成問題,在此記錄一下,引以為誡。
關於如何unlock的問題
其實我發現數據庫被Locked了之後的第一反應時是如何去unlock。不過很遺憾,我沒有在現有的android API裏找到類似於數據庫lock/unlock的方法。不過android 倒是提供了一個
isDbLockedByCurrentThread() 這樣的方法用於檢測當前數據庫是否有被lock, 網上很多的數據庫打開方式都是類似於下列代碼:
private synchronized SQLiteDatabase openDatabase() { if (mOpenCounter.incrementAndGet() == 1 || mDatabase == null) { mDatabase = mDBHelper.getWritableDatabase(); } return mDatabase; }
這種方式雖然貌似解決了多個 數據庫connection的問題,但是個人以為還是不太完善,像我遇到的這種情況,最終crash就發生在
mDatabase = mDBHelper.getWritableDatabase();
這行代碼裏。因此個人以為保險起見,可以在上述代碼之前添加一個諸如下列的代碼:
private synchronized SQLiteDatabase openDatabase() { if (mOpenCounter.incrementAndGet() == 1 || mDatabase == null) { if (mDatabase == null) { mDatabase = mDBHelper.getWritableDatabase(); } else { while (mDatabase.isDbLockedByCurrentThread()) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } mDatabase = mDBHelper.getWritableDatabase(); } } return mDatabase; }
可能會更好點,個人看法。
sqlite: Error Code : 5 (SQLITE_BUSY) (database is locked (code 5): , while compiling: PRAGMA journal_mode)