Android SQLiteDatabaseLockedException: database is locked

問題場景描述:

在頁面中用到了ViewPager控件,ViewPager中的內容分別是兩個ListView,兩個ListView的數據都來自本地數據庫(先從網絡下載數據,然後更新本地數據庫),在實際的使用過程中發現會出現SQLiteDatabaseLockedException: database is locked的問題。

經網上搜索資料,發現是讀寫數據庫時存在的同步問題,所以採用單例+同步鎖的方法,並且在每次數據庫操作後都關閉數據庫,經測試後發現沒有在出現上述問題。

以下是兩個主類

DBHelper.java(這個類用來管理數據庫)

public class DBHelper extends SQLiteOpenHelper {

	private final String TAG = this.getClass().getSimpleName();
	
	public final static String DATABASE_NAME = "test.db";
	public final static String TABLE = "table";
	public final static int DATABASE_VERSION = 2;

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

	private static DBHelper mInstance;

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

	@Override
	public void onCreate(SQLiteDatabase db) {
		try {
			db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE
					+ "(id INTEGER PRIMARY KEY ,data BLOB)");
		} catch (SQLiteException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {	
		
		// 刪除原來的數據表
		db.execSQL("DROP TABLE IF EXISTS " + TABLE);
		// 重新創建
		onCreate(db);
	}

	public static byte[] objectToBytes(Object obj) throws Exception {

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		ObjectOutputStream sOut = new ObjectOutputStream(out);
		sOut.writeObject(obj);
		sOut.flush();
		byte[] bytes = out.toByteArray();
		return bytes;
	}

	public static Object bytesToObject(byte[] bytes) throws Exception {

		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
		ObjectInputStream sIn = new ObjectInputStream(in);
		return sIn.readObject();
	}
}

DBStudentManager類(這裏可以定義自己的管理類)
public class DBStudentManager {

	private DBHelper helper;
	private SQLiteDatabase db;

	public DBStudentManager(Context context) {
		helper = DBHelper.getInstance(context);
		db = helper.getWritableDatabase();
	}

	// 插入
	private void insert(Student student) {

		synchronized (helper) {
			// 看數據庫是否關閉
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			// 開始事務
			db.beginTransaction();
			try {
				db.execSQL(
						"INSERT INTO " + DBHelper.TABLE + " VALUES(?,?)",
						new Object[] { student.mID,
								DBHelper.objectToBytes(student) });
				db.setTransactionSuccessful(); // 設置事務成功完成
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
				db.close();
			}
		}
	}

	// 更新
	private void update(Student student) {

		synchronized (helper) {
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			db.beginTransaction();
			try {
				db.execSQL("UPDATE " + DBHelper.TABLE
						+ "SET data = ? WHERE id = ?",
						new Object[] { DBHelper.objectToBytes(student),
								student.mID });
				db.setTransactionSuccessful(); // 設置事務成功完成
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
				db.close();
			}
		}
	}

	// 同步
	public void synchronous(List<Student> students) {
		if (students == null) {
			return;
		}
		for (Student student : students) {
			if (query(student.mID) == null) {
				insert(student);
			} else {
				update(student);
			}
		}
	}

	// 刪除指定數據
	public void delete(String id) {

		synchronized (helper) {
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			db.beginTransaction();
			try {
				db.execSQL("DELETE FROM " + DBHelper.TABLE + " WHERE id = ? ",
						new String[] { id });
				db.setTransactionSuccessful();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
				db.close();
			}
		}
	}

	// 刪除所有數據
	public void delete() {

		synchronized (helper) {
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			db.beginTransaction();
			try {
				db.execSQL("DELETE * FROM " + DBHelper.TABLE);
				db.setTransactionSuccessful();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
				db.close();
			}
		}
	}

	// 查找所有的Students
	public List<Student> query() {

		List<Student> students = new ArrayList<Student>();
		synchronized (helper) {
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			Cursor c = queryTheCursor();
			Student student = null;
			try {
				while (c.moveToNext()) {

					byte[] bytes = c.getBlob((c.getColumnIndex("data")));
					student = (Student) DBHelper.bytesToObject(bytes);
					students.add(student);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				c.close();
			}
		}
		return students;
	}

	// 查找指定ID的Student
	public Student query(String id) {

		Student student = null;
		synchronized (helper) {
			if (!db.isOpen()) {
				helper.getWritableDatabase();
			}
			Cursor c = queryTheCursor(id);
			try {
				while (c.moveToNext()) {

					byte[] bytes = c.getBlob((c.getColumnIndex("data")));
					student = (Student) DBHelper.bytesToObject(bytes);
					break;
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				c.close();
			}
		}
		return student;
	}

	// 獲取遊標
	public Cursor queryTheCursor(String id) {
		Cursor c = db.rawQuery("SELECT FROM " + DBHelper.TABLE
				+ " WHERE id = ?", new String[] { id });
		return c;
	}

	// 獲取遊標
	public Cursor queryTheCursor() {
		Cursor c = db.rawQuery("SELECT * FROM " + DBHelper.TABLE);
		return c;
	}

	class Student {

		String mID;
		String mName;
		int mAge;
	}
}

使用繼承ContentProvider的方式,也可以解決該異常。

   但是這種方式有2個弊端:

     1.使用繼承ContentProvider方式,會將項目的數據庫暴露給第三方。一般系統級應用,如存儲聯繫人,       存儲短信的數據庫纔有必要暴露給第三方。

     2.使用方式不如直接繼承SQLiteOpenHelper對象簡單。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章