基于Android中Looper , Handler , Message的线程池,轻松解决Sqlite数据库的线程安全问题
SQLite是基于单一磁盘文件的数据库,所以SQLite是文件级别的锁:多个线程可以同时读,但是同时只能有一个线程写。所以多线程操作时往往容易出现问题。
但矛盾的是,操作SQLite往往是一个耗时的过程,如果在主线程中进行,数据量一大就容易发生臭名昭著的ANR异常。
怎么办呢?小弟封装了一个基于Looper , Handler , Message的线程池,主要思路和其它线程池没有什么两样,重点是当分配任务到线程池中执行时,可以通过id指定在哪个线程运行,这就可以很好的解决上面提到的Sqlite数据库的线程安全问题,操作SQLite的任务固定指定一个id,让它们在同一个线程中执行,这样就不用担心多个线程对同一个SQLite数据库同时操作产生的竞争问题,其次对Sqlite数据库的操作放到了其它线程,不在主线程,也很好的解决了ANR异常问题。
好了,废话不多说,先上代码吧:
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.SparseArray;
import com.zjg.smart.android.debug.DetailedLog;
import com.zjg.smart.utils.DebugUtils;
import com.zjg.test.Common;
public final class AndroidThreadPool {
private SparseArray<Worker> pool = new SparseArray<Worker>();
private static class SingletonHelper {
public static AndroidThreadPool instance = new AndroidThreadPool();
}
private AndroidThreadPool() {
}
public static AndroidThreadPool getInstance() {
return SingletonHelper.instance;
}
public static abstract class Task implements Runnable {
private boolean stop = false;
private boolean succeed = false;
private String errMsg = "";
synchronized protected final void setErrMsg(String errMsg) {
succeed = false;
this.errMsg = errMsg;
}
synchronized protected final String getErrMsg() {
return errMsg;
}
synchronized protected final boolean isSucceed() {
return succeed;
}
synchronized protected final boolean isStop() {
return stop;
}
synchronized final void initialize() {
succeed = true;
errMsg = "";
stop = false;
}
synchronized final void stop() {
stop = true;
}
/**
* 寄送一个任务到主线程,不等其完成,继续往下执行
*
* @param runnable
* 被寄送的任务
*/
protected final void postMainThread(Runnable runnable) {
if (runnable == null) {
setErrMsg(DebugUtils.STACK_TRACE() + "runnable == null");
return;
}
new Handler(Looper.getMainLooper()).post(runnable);
return;
}
private Object waitMainThreadSyncLock = new Object();
/**
* 寄送一个任务到主线程,并等待其完成,才继续往下执行
*
* @param runnable
* 被寄送的任务
*/
protected final void waitMainThread(final Runnable runnable) {
if (runnable == null) {
setErrMsg(DebugUtils.STACK_TRACE() + "runnable == null");
return;
}
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
setErrMsg(DebugUtils.STACK_TRACE()
+ "Can't be called in the main thread.");
return;
}
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// TODO自动生成的方法存根
runnable.run();
synchronized (waitMainThreadSyncLock) {
waitMainThreadSyncLock.notifyAll();
}
}
});
synchronized (waitMainThreadSyncLock) {
try {
waitMainThreadSyncLock.wait();
} catch (InterruptedException e) {
// TODO自动生成的 catch 块
e.printStackTrace();
}
}
}
/**
* 子类可重载,返回运行任务的线程id
*
* @return
*/
synchronized public int getRunThreadId() {
return 0;
}
/**
* 子类可重载,任务运行前的预处理,只会在主线程中调用
*/
protected void pretreatment() {
};
/**
* 子类必须重载,当任务成功时被调用,只会在主线程中调用
*/
protected abstract void onSucceed();
/**
* 子类必须重载,当任务失败时被调用,只会在主线程中调用
*
* @param msg
* 失败消息
*/
protected abstract void onFailed(String msg);
/**
* 子类可重载,当任务被终止时被调用,只会在主线程中调用
*/
protected void onStop() {
}
}
synchronized public void execution(Task task) {
int id = task.getRunThreadId();
Worker worker = pool.get(id);
if (worker == null) {
worker = new Worker();
pool.put(id, worker);
}
worker.execution(task);
}
synchronized public void stopThread(int id) {
Worker worker = pool.get(id);
if (worker != null) {
worker.stop();
}
}
synchronized public void destroy() {
for (int i = 0, size = pool.size(); i < size; i++) {
Worker worker = pool.valueAt(i);
if (worker != null) {
worker.destroy();
}
}
pool.clear();
}
class Worker {
private Task task = null;
private Object taskSyncLock = new Object();
private Handler handler = null;
private Object handlerSyncLock = new Object();
private Thread thread = new Thread() {
@Override
public void run() {
super.run();
Looper.prepare();
synchronized (handlerSyncLock) {
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
final Task task = (Task) msg.obj;
if (task != null) {
synchronized (taskSyncLock) {
Worker.this.task = task;
}
task.initialize();
task.waitMainThread(new Runnable() {
@Override
public void run() {
// TODO自动生成的方法存根
if (task.isSucceed()) {
task.pretreatment();
}
}
});
if (task.isSucceed()) {
task.run();
}
if (!task.isStop()) {
new Handler(Looper.getMainLooper())
.post(new Runnable() {
@Override
public void run() {
if (task.succeed) {
task.onSucceed();
} else {
task.onFailed(task.errMsg);
}
}
});
} else {
new Handler(Looper.getMainLooper())
.post(new Runnable() {
@Override
public void run() {
task.onStop();
}
});
}
synchronized (taskSyncLock) {
Worker.this.task = null;
}
}
}
};
handlerSyncLock.notifyAll();
}
Looper.loop();
}
};
void execution(Task task) {
if (handler == null) {
if (thread != null) {
thread.start();
}
if (handler == null) {
synchronized (handlerSyncLock) {
if (handler == null) {
try {
handlerSyncLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Message msg = handler.obtainMessage();
msg.obj = task;
handler.sendMessage(msg);
}
void stop() {
if (thread != null && thread.isAlive()) {
if (task != null) {
synchronized (taskSyncLock) {
if (task != null) {
task.stop();
task = null;
}
}
}
}
}
void destroy() {
if (thread != null && thread.isAlive()) {
if (task != null) {
synchronized (taskSyncLock) {
if (task != null) {
task.stop();
task = null;
}
}
}
if (handler != null) {
synchronized (handlerSyncLock) {
if (handler != null) {
handler.getLooper().quit();
}
}
}
thread.interrupt();
}
}
}
}
代码有点多,但不难理解,我来解释一下:
客户代码通过扩展Task,重载run()、getRunThreadId()、pretreatment()、onSucceed()、onFailed(String msg)、onStop()方法来完成任务。
Worker,顾名思义,工作者,它维护一个线程,并在线程中开启一个Looper.loop(),等待任务的到来。
线程池采用单例模式,并通过id维护一个Worker的SparseArray集合,当execution一个Task时,通过Task的成员方法getRunThreadId()取得id,通过id取得对应的Worker,通过此Worke的线程的Handle把Task寄送到线程中执行。
思路就这么简单。
下面是我的一个应用场景:
AndroidThreadPool.getInstance().execution(new Task() {
private int max = 0;
private int progress = 0;
@Override
public void run() {
InputStream inputStream = null;
OutputStream out = null;
try {
File dir = AppUtils.getAppFileDir(
ExceptionCatchService.this, EXCEPTION_REPORT_TAG);
File file = new File(dir, new SimpleDateFormat(
"yyyy-MM-dd-HH-mm-ss").format(new Date())
+ "."
+ EXCEPTION_REPORT_TAG);
if (!file.exists()) {
file.createNewFile();
}
out = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int length = -1;
inputStream = new ByteArrayInputStream(exception
.getBytes("utf-8"));
max = inputStream.available();
while ((length = inputStream.read(buffer)) != -1) {
progress += length;
new Handler(Looper.getMainLooper())
.post(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(
RECEIVER_ACTION);
intent.putExtra(PROGRESS_TAG, progress);
intent.putExtra(MAX_TAG, max);
intent.putExtra(MSG_TAG, "");
sendBroadcast(intent);
}
});
out.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
protected void onSucceed() {
Intent intent = new Intent(RECEIVER_ACTION);
intent.putExtra(PROGRESS_TAG, progress);
intent.putExtra(MAX_TAG, max);
intent.putExtra(MSG_TAG, "保存异常报告完毕。");
sendBroadcast(intent);
ExceptionCatchService.this.stopSelf();
}
@Override
protected void onStop() {
Intent intent = new Intent(RECEIVER_ACTION);
intent.putExtra(PROGRESS_TAG, progress);
intent.putExtra(MAX_TAG, max);
intent.putExtra(MAX_TAG, "保存异常报告被终止。");
sendBroadcast(intent);
ExceptionCatchService.this.stopSelf();
}
@Override
protected void onFailed(String msg) {
// TODO自动生成的方法存根
}
});
可见run()、onSucceed()、onFailed(String msg)是必须重载的,getRunThreadId()没有重载,采用默认的id值0。
下面是我封装的常用的SQLite操作,以供参考。
SqliteDatabaseTask.java,SQLite操作的基类:
public abstract class SqliteDatabaseTask extends Task {
// log_begin
private static final DetailedLog Log = new DetailedLog();
private static final String LOG_TAG = Common.LOG_TAG;
// log_end
protected File databaseFile = null;
protected abstract boolean asyncHandleDatabase(SQLiteDatabase database);
public SqliteDatabaseTask(File databaseFile) {
if (databaseFile == null) {
throw new NullPointerException("\"databaseFile == null\"");
}
this.databaseFile = databaseFile;
}
@Override
public void run() {
// TODO自动生成的方法存根
SQLiteDatabase database = null;
try {
database = SqliteDatabaseUtils.openDatabase(databaseFile);
if (database == null) {
setErrMsg(this + ",打开数据库文件\"" + databaseFile.getAbsolutePath()
+ "\"失败。");
return;
}
if (!asyncHandleDatabase(database)) {
return;
}
} finally {
if (database != null) {
database.close();
}
}
}
}
SqliteDatabaseQueryTask.java,用于查询:
public abstract class SqliteDatabaseQueryTask extends SqliteDatabaseTask {
public SqliteDatabaseQueryTask(File databaseFile) {
super(databaseFile);
}
@Override
protected final boolean asyncHandleDatabase(SQLiteDatabase database) {
// TODO
waitMainThread(new Runnable() {
@Override
public void run() {
// TODO自动生成的方法存根
m_sqlString = getSqlString();
}
});
if (m_sqlString == null) {
setErrMsg(this + ":\"getSqlString()\" return null.");
return false;
}
if (m_sqlString.length() == 0) {
setErrMsg(this + ":\"getSqlString()\" return \"\".");
return false;
}
waitMainThread(new Runnable() {
@Override
public void run() {
// TODO自动生成的方法存根
m_args = getArgs();
}
});
Cursor cursor = null;
try {
cursor = database.rawQuery(m_sqlString, m_args);
if (cursor == null) {
setErrMsg(this
+ ":查询数据库时返回的\"cursor\" = null, \"sqlString\" = \""
+ m_sqlString
+ "\", \"args\" = "
+ (m_args == null ? null : Arrays.toString(m_args)
+ "."));
return false;
}
if (!asycHandleCursor(cursor)) {
return false;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return true;
}
protected abstract boolean asycHandleCursor(Cursor cursor);
private String m_sqlString = "";
protected abstract String getSqlString();
private String[] m_args = null;
protected abstract String[] getArgs();
}
SqliteDatabaseSaveTask.java,用于保存:
public abstract class SqliteDatabaseSaveTask extends SqliteDatabaseTask {
// log_begin
private static final DetailedLog Log = new DetailedLog();
private static final String LOG_TAG = Common.LOG_TAG;
// log_end
public SqliteDatabaseSaveTask(File databaseFile) {
super(databaseFile);
}
protected abstract boolean asyncSave(SQLiteDatabase database);
@Override
protected final boolean asyncHandleDatabase(final SQLiteDatabase database) {
database.beginTransaction();
try {
if (!asyncSave(database)) {
return false;
}
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
return true;
}
}