最近在优化之前的客户端软件-号码通,应用是基于数据库查询的,数据库是单独分开发布的,数据库除了查询操作之外,还有添加、更新、删除操作,而Sqlite 数据库对多线程并发不支持(至少不够好,多线程去操作容易出问题),故需要对 数据库的读写进行同步以保证线程安全。
DBHelper.java
package com.example.sqlitetest;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
public class DBHelper {
//数据库路径
private static final String DATABASE_PATH = Environment
.getExternalStorageDirectory().getAbsolutePath() + "/yulore/";
private ReadWriteLock lock = new ReentrantReadWriteLock(true);
private Lock readLock = lock.readLock();
private Lock writeLock = lock.writeLock();
/**
* 获取用于进行 读操作的 SQLiteDatabase
* @param databaseFileName 数据库名称
* @return
*/
public SQLiteDatabase getReadableDataBase(String databaseFileName) {
readLock.lock();
try {
// 得到数据库的完整路径名
String databasePath = DATABASE_PATH + databaseFileName;
return SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
}finally{
readLock.unlock();
}
}
/**
* 获取用于进行 写操作的 SQLiteDatabase
* @param databaseFileName
* @return
*/
public SQLiteDatabase getWritableDataBase(String databaseFileName) {
writeLock.lock();
try {
// 得到数据库的完整路径名
String databasePath = DATABASE_PATH + databaseFileName;
return SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
} finally{
writeLock.unlock();
}
}
}
使用ReadWriteLock对Sqlite 数据库的读写操作进行同步,保证不会因为一个线程写 Sqlite 数据库导致其他读线程因为Sqlite 数据库被锁定而出错的情况。
模拟读线程 ReadThread.java
package com.example.sqlitetest.thread;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.example.sqlitetest.DBHelper;
public class ReadThread implements Runnable {
private DBHelper helper;
public ReadThread(DBHelper helper) {
this.helper = helper;
}
@Override
public void run() {
queryDataBase();
}
private void queryDataBase() {
SQLiteDatabase db = helper.getReadableDataBase("ypdata");
int count = 0;
Cursor cursor = null;
try {
if (db.isOpen()) {
String sql = "SELECT count(*) FROM DialHistory";
try {
Thread.sleep(200); //模拟等待
} catch (InterruptedException e) {
e.printStackTrace();
}
cursor = db.rawQuery(sql, null);
if (cursor.moveToNext()) {
count = cursor.getInt(0);
Log.e("ReadThread", Thread.currentThread().getName()+" query table DialHistory count = " + count);
}
}
} finally {
if (cursor != null) {
try {
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
}
db.close();
}
}
// private void queryDataBase() {
//
// SQLiteDatabase db = helper.getReadableDataBase("ypdata");
//
// int count = 0;
//
// Cursor cursor = null;
// try {
// if (db.isOpen()) {
// String sql = "SELECT count(*) FROM City WHERE selected =1";
// cursor = db.rawQuery(sql, null);
// if (cursor.moveToNext()) {
//
// count = cursor.getInt(0);
//
// Log.e("ReadThread", "download city count = " + count);
// }
//
// try {
// Thread.sleep(200); //模拟等待
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// } finally {
// if (cursor != null) {
// try {
// cursor.close();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// db.close();
// }
// }
}
模拟写线程 WriteThread.java
package com.example.sqlitetest.thread;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.example.sqlitetest.DBHelper;
public class WriteThread implements Runnable {
private DBHelper helper;
private int sequence;
public WriteThread(DBHelper helper,int sequence) {
this.helper = helper;
this.sequence = sequence;
}
@Override
public void run() {
updateDataBase();
}
private void updateDataBase() {
SQLiteDatabase db = helper.getWritableDataBase("ypdata");
try {
if (db.isOpen()) {
db.execSQL("INSERT INTO DialHistory (City,DialType,ShopID,TelNumberID,LastDial)"
+ " VALUES ("
+ sequence
+ ","
+ sequence
+ ","
+ 123456
+ ","
+ 123456
+ ","
+ "DATETIME('now','localtime'))");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("WriteThread", Thread.currentThread().getName()+" add table DialHistory success..."+sequence);
}
} finally {
db.close();
}
}
}
测试demo
package com.example.sqlitetest;
import com.example.sqlitetest.thread.ReadThread;
import com.example.sqlitetest.thread.WriteThread;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
private Button bt_read_write;
private DBHelper helper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById();
initDB();
setListener();
}
private void initDB() {
helper = new DBHelper();
}
private void setListener() {
bt_read_write.setOnClickListener(this);
}
private void findViewById() {
bt_read_write = (Button) findViewById(R.id.bt_read_write);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_read_write:
Log.i("MainActivity", "开始测试 DB 并发性...");
//4个读线程
new Thread(new ReadThread(helper)).start();
new Thread(new ReadThread(helper)).start();
new Thread(new ReadThread(helper)).start();
new Thread(new ReadThread(helper)).start();
//3个写线程
new Thread(new WriteThread(helper,1)).start();
new Thread(new WriteThread(helper,2)).start();
new Thread(new WriteThread(helper,3)).start();
break;
default:
break;
}
}
}
布局文件activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<Button
android:id="@+id/bt_read_write"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/test_read_write" />
</RelativeLayout>
如果应用中使用 SqliteOpenHelper来操作数据导致的android.database.sqlite.SQLiteException: database is locked这样的异常,请参看这篇文章
http://bbs.51cto.com/thread-990260-1.html
个人能力有限,程序有不对的地方还请大家多多指教,谢谢!