1. 程式人生 > >GreenDao自帶非同步操作類簡析

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即可解決。

更新一下,附上一張類圖:
這裡寫圖片描述