1. 程式人生 > >sqlite: Error Code : 5 (SQLITE_BUSY) (database is locked (code 5): , while compiling: PRAGMA journal_mode)

sqlite: Error Code : 5 (SQLITE_BUSY) (database is locked (code 5): , while compiling: PRAGMA journal_mode)

nta exce select char 解決 color man ssi exceptio

今天遇到了一個很奇怪的問題,登錄完成後,程序會莫名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)