GreenDao自帶非同步操作類簡析
AsyncSession:
GreenDao提供一個非同步操作的統一介面類AsyncSession,它提供了你所需要的所有非同步操作方法。
你可以通過呼叫DaoSession#startAsyncSession()來獲得一個AsyncSession例項。
public AsyncSession startAsyncSession() {
return new AsyncSession(this);
}
在AsyncSession的建構函式中,會賦值兩個全域性變數:一個是傳參進來的DaoSession,另一個是建立的AsyncOperationExecutor物件。
public AsyncSession(AbstractDaoSession daoSession) {
this.daoSession = daoSession;
this.executor = new AsyncOperationExecutor();
}
那麼AsyncOperationExecutor是什麼?
AsyncOperationExecutor是一條管理著阻塞佇列的執行緒,由它來進行實際的db操作,它的內部實現有點類似於Looper(後面再詳解)。AsyncSession作為一個介面類,會將所有非同步任務組裝釋出給AsyncOperationExecutor進行具體的操作。
有非同步任務來時,AsyncSession會將非同步任務封裝成AsyncOperation物件,再將AsyncOperation物件加入到AsyncOperationExecutor物件的阻塞佇列中,再走AsyncOperationExecutor的內部邏輯。
private AsyncOperation enqueueDatabaseOperation(OperationType type, Object param, int flags) {
SQLiteDatabase database = daoSession.getDatabase();
AsyncOperation operation = new AsyncOperation(type, null, database, param, flags | sessionFlags);
executor.enqueue(operation);
return operation;
}
這個AsyncOperation包含有下列資訊:
db操作的型別OperationType :增、刪、改、查(這裡細分的多,會根據具體操作來返回對應的結果);
SQLiteDatabase 例項;
操作的引數parameter:sql語句的where判斷;
每一個AsyncSession物件都擁有一個AsyncOperationExecutor 例項,,而每一個AsyncOperationExecutor 物件都管理著一個阻塞佇列。也就是說,在每個類裡頭建立的AsyncSession物件將在一條執行緒中處理一個佇列的工作,這樣至少能保證當前類中只會建立一條工作執行緒迴圈處理非UI操作。
AsyncOperationExecutor中工作流程:
AsyncOperationExecutor本身運行於執行緒池生成的某一條執行緒中,管理著BlockingQueue,在有AsyncOperation新增時,開啟任務執行,設定標記executorRunning =true:表示執行緒為執行狀態。AsyncOperationExecutor執行時不停的從BlockingQueue中取出有AsyncOperation進行預定義的增刪改查操作(executeOperationAndPostCompleted(),如佇列為空則執行緒阻塞),在操作完成後將結果賦值給有AsyncOperation.result變數(如無則不賦值),再將帶任務結果的AsyncOperation返回給監聽類。
//結果返回,回到主執行緒中執行
@Override
public boolean handleMessage(Message msg) {
AsyncOperationListener listenerToCall = listenerMainThread;
if (listenerToCall != null) {
listenerToCall.onAsyncOperationCompleted((AsyncOperation) msg.obj);
}
return false;
}
//AsyncOperationExecutor主要邏輯程式碼
@Override
public void run() {
try {
try {
while (true) {
AsyncOperation operation = queue.poll(1, TimeUnit.SECONDS);
if (operation == null) {
synchronized (this) {
// Check again, this time in synchronized to be in sync with enqueue(AsyncOperation)
operation = queue.poll();
if (operation == null) {
// set flag while still inside synchronized
executorRunning = false;
return;
}
}
}
if (operation.isMergeTx()) {
// 等一些時間,看有沒有新加入的可在同一事務中執行的任務
AsyncOperation operation2 = queue.poll(waitForMergeMillis, TimeUnit.MILLISECONDS);
if (operation2 != null) {
if (operation.isMergeableWith(operation2)) {
mergeTxAndExecute(operation, operation2);
} else {
// 不能合併事務操作的,就分開執行下,否則在上面程式碼中合併操作
executeOperationAndPostCompleted(operation);
executeOperationAndPostCompleted(operation2);
}
continue;
}
}
executeOperationAndPostCompleted(operation);
}
} catch (InterruptedException e) {
DaoLog.w(Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
AsyncOperationExecutor有一個小操作,在從佇列中拿到AsyncOperation處理前會先等待一定時間去獲取下一個AsyncOperation,判斷是否與其可以進行使用同一事務的合併操作,必竟事務操作代價是比較大的。這裡有預先設定合併操作的最大數量,如果有較大的批量操作可能超出限定時,還是建議使用GreenDao提供的其它事務操作方法,必竟這裡只是動態的判斷是否有可合併的資料庫操作,判斷與處理的過程中會增加消耗。
下面是簡單的非同步操作示例:
AsyncSession async = DbMgr.daoSession.startAsyncSession();
//不關心操作結果時,可不設定lisnter
async.setListenerMainThread(new AsyncOperationListener() {
@Override
public void onAsyncOperationCompleted(AsyncOperation arg0) {
if (isFinishing())
return;
//對db結果的UI操作
}
});
async.queryList(builder.build());
如果對操作結果不關心且需進行多項操作時,可以使用這種自動開啟事務的方式:
DbMgr.daoSession.startAsyncSession().runInTx(new Runnable() {
@Override
public void run() {
//DELETE
//DELETE
//UPDATE
}
});
使用過程中碰到:
“Method may be called only in owner thread, use forCurrentThread to get an instance for this thread”
問題在於AsyncOperationExecutor 漏了
((Query) operation.parameter).forCurrentThread().list()
這個升級到2.1即可解決。
更新一下,附上一張類圖: