SQLiteException:database is locked異常の解決

4853 ワード

sqliteデータベースでは、同じ時刻に複数のプロセス/スレッドの読み取りが許可されますが、同じ時刻に1つのスレッドの書き込みのみが許可されます.書き込み操作を操作すると、データベースファイルが煩雑になり、他の読み取り/書き込み操作がブロックされます.ブロックが5秒を超えると(デフォルトは5秒で、sqliteを再コンパイルしてタイムアウト時間を変更できます)、database is lockedエラーが表示されます.
SQLiteはファイルレベルのロックです.複数のスレッドは同時に読むことができますが、同時に1つのスレッドしか書くことができません.AndroidはSqliteOpenHelperクラスを提供し、Javaのロックメカニズムを追加して呼び出します.
マルチスレッド同時読み書き(ここでは異なるスレッドが異なるHelperインスタンスを使用することを指す)すると、後でandroidに遭遇する.database.sqlite.SQLiteException:database is lockedという異常.このような問題に対して、解決策は、sqlite接続の単一例を維持し、単一のSqliteOpenHelperインスタンスを維持し、同時にすべてのデータベース操作の方法にsynchronizedキーワードを追加することである.つまり、データベースを読み書きする際に発生する同期の問題なので、単一の例+同期ロックの方法を採用し、データベースの操作のたびにデータベースを閉じます.
1、DBHelper用単例
public class DBHelper extends SQLiteOpenHelper {

    private final String TAG = "DBHelper";
    private final static String DATABASE_NAME = "stu.db";
    private final static String TABLE_NAME = "student";
    private final static int DATABASE_VERSION = 1;
    private static DBHelper mInstance;

    public DBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME
                + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, name, age)");
    }

    
    //    
    public synchronized static DBHelper getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new DBHelper(context.getApplicationContext());
        }
        return mInstance;
    }


    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //         
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        //     
        onCreate(db);
    }
}


2、書き込み操作と同期ロック
public class StudentManager {

    //          
    private static final String TABLE_NAME = "student";
    private static final String KEY_ID = "_id";
    private static final String KEY_NAME = "name";
    private static final String KEY_AGE = "age";

    private DBHelper dbHelper;

    public StudentManager(Context context) {
        //   DbHelper
        dbHelper = DBHelper.getInstance(context);
    }

    //       
    public synchronized boolean insert(Student stu) throws Exception {
        //        ,      。  ,    。
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        //       
        ContentValues cv = new ContentValues();
        cv.put(KEY_NAME, stu.getName());
        cv.put(KEY_AGE, stu.getAge());
        long id = db.insert(TABLE_NAME, null, cv);
        //     
        db.close();
        if (id == -1) {
            return false;//    
        }
        return true;
    }

    //       
    public List getAll() throws Exception {
        List stus = new ArrayList<>();
        //    ,    
        //     
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        //      
        Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, null);
        //Cursor   ( ),        
        //        ,     id
        int idIndex = cursor.getColumnIndex(KEY_ID);
        int nameIndex = cursor.getColumnIndex(KEY_NAME);
        int ageIndex = cursor.getColumnIndex(KEY_AGE);
        while (cursor.moveToNext()) {
            //        ,          , 0  
            int id = cursor.getInt(idIndex);
            String name = cursor.getString(nameIndex);
            int age = cursor.getInt(ageIndex);
            Student stu = new Student(name, age);
            stus.add(stu);
        }
        //    
        cursor.close();
        //     
        db.close();
        return stus;
    }
}


まとめ
1、マルチスレッドアクセスによるデータベースロック.Aスレッドが現在のデータベースにアクセスしている場合、Bスレッドもデータベースにアクセスする必要があります.これにより、Bスレッドでは、上記のような異常が発生します.データベースアクセスを提供する方法を同期化し、非同期呼び出し時に問題が発生しないようにする必要があります.
public static synchronized DBConnection getConnection(String connectionName) throws Exception { 
String pathFile = getPath() + connectionName;//     data  
return new DBConnection(SQLiteDatabase.openDatabase(pathFile, 
null, SQLiteDatabase.OPEN_READWRITE)); }

synchronizedキーワードを使用してデータベース接続の取得方法を修飾するか、isDbLockedByOtherThreadsメソッドを使用してデータベースがロックされているかどうかを判断し、一定の時間待ってからアクセスします.
2、・sqlite自身の問題は、デバッグ時にログコンソールに頻繁に上記の異常が発生していることを発見することがありますが、プログラムを実行している間にこの問題はありません.このような現象はResultSetをデバッグする時にも発生します.資料を調べてみると、sqliteデータが完全にスレッドの安全ではないため、いくつかの場所でわけがわからない問題が発生します.このような状況に遭遇したら、それは、データベース接続の場所にブレークポイントを打たないことしかできません.
3、マルチスレッド同時読み書き(ここでは異なるスレッドが異なるHelperインスタンスを使用することを指す)すると、後でdatabaseに遭遇する.sqlite.SQLiteException:database is lockedという異常.このような問題に対して、解決策はkeep single sqlite connectionであり、単一のSqliteOpenHelperインスタンスを維持しながら、すべてのデータベース操作の方法にsynchronizedキーワードを追加することである.