SQLite資料庫學習小結(2)

來源:互聯網
上載者:User

標籤:

3. SQLite的Frameworks層實現3.1 Frameworks層架構

    Android系統方便應用使用,在Frameworks層中封裝了一套Content架構,之所以叫Content架構而不叫資料庫架構之類的,是因為這裡Content不一定是來自資料庫的內容,也可以是來自其他資料來源的內容,開發人員只需要知道如何使用ContentResovler和ContentProvider就可以在應用進程之間共用資料了。

  這裡我們只討論資料來源是資料庫的ContentProvider,開發人員需要實現一個SQLiteOpenHelper的衍生類別,它使用了一系列SQLite相關的類封裝了native層的SQLite動態庫的介面方法,那麼SQLite在Frameworks層是如何封裝的,我們在使用SQLite時又需要注意些什麼呢?

    我們先來看一看基於SQLite的Content架構的整體架構:

    Android系統在Frameworks層

  1、ContentResolver:Content架構的用戶端,對應用提供增、刪、改、查的介面方法,通過Authority指定目標Content服務,並串連到服務端執行資料操作。   2、ContentProvider:Content架構的服務端,Android應用四大組件之一,通過Authority註冊到PMS,在運行時有用戶端請求時由AMS調度來響應用戶端的資料操作。   3、SQLiteOpenHelper:管理SQLite的協助類,提供擷取SQLIteDatabase執行個體的方法,它會在第一次使用資料庫時調用擷取執行個體方法時建立SQLiteDatabase執行個體,並且處理資料庫版本變化,開發人員在實現ContentProvider時都要實現一個自訂的SQLiteOpenHelper類,處理資料的建立、升級和降級。   注意: 

    (1)不管是調用getWritableDatabase方法還是getReadableDatabase方法,SQLIteOpenHelper都會以可讀寫入模式開啟資料庫。

    (2)如果應用程式想以WAL模式開啟資料庫,可在自訂SQLiteOpenHelper類的構造方法中調用setWriteAheadLoggingEnabled(true)。 

    SQLiteOpenHelper.java

     

  4、SQLiteDatabase:代表一個開啟的SQLite資料庫,提供了執行資料庫操作的介面方法。如果不需要在進程之間共用資料,應用程式也可以自行建立這個類的執行個體來讀寫SQLite資料庫。   5、SQLiteSession:SQLiteSession負責管理資料庫串連和事務的生命週期,通過SQLiteConnectionPool擷取資料庫連接來執行具體的資料庫操作。 

  6、SQLiteConnectionPool:資料庫連接池,管理所有開啟的資料庫連接(Connection)。所有資料庫連接都是通過它來開啟,開啟後會加入串連池,在讀寫資料庫時需要從串連池中擷取一個資料庫連接來使用。 

  7、SQLiteConnection:代表了資料庫連接,每個Connection封裝了一個native層的sqlite3執行個體,通過JNI調用SQLite動態庫的介面方法操作資料庫,Connection要麼被Session持有,要麼被串連池持有。 

  8、CursorFactory:可選的Cursor工廠,可以提供自訂工廠來建立Cursor。 

  9、DatabaseErrorHandler:可選的資料庫異常處理器(目前僅處理資料庫Corruption),如果不提供,將會使用預設的異常處理器。 

  10、SQLiteDatabaseConfiguration:資料庫配置,應用程式可以建立多個到SQLite資料庫的串連,這個類用來保證每個串連的配置都是相同的。 

  11、SQLiteQuery和SQLiteStatement:從抽象類別SQLiteProgram派生,封裝了SQL語句的執行過程,在執行時自動組裝待執行的SQL語句,並調用SQLiteSession來執行資料庫操作。這兩個類的實現應用了設計模式中的命令模式。 

3.2 關鍵模組實現

    本節介紹幾個關鍵模組的實現和使用時需要注意的事項。

3.2.1 SQLiteSession

  Android系統Frameworks層的資料庫讀寫操作都是通過SQLiteSession完成的,SQLiteSession負責管理資料庫串連和事務的生命週期。

  一個SQLiteDatabse執行個體可以同時持有多個活躍的Session(但是為防止死結,每個線程只能持有一個DB的Session),每個Session在執行SQL語句時擷取資料庫連接,在SQL語句執行結束後釋放資料庫連接,Session只有在只執行SQL語句期間保持資料庫連接,執行完後就釋放了。這個特性也是串連池的實現基礎。

SQLiteSession.java

  如果串連池中所有串連都已指派出去了,那麼擷取串連的SQLiteSession會阻塞直到有可用串連為止。

  所以,在使用SQLite時需要注意以下幾點: 

  (1)不要在UI線程中執行資料庫操作。 

  (2)執行的事務盡量短。 

  (3)如果讀寫事務很長,可以考慮使用yieldTransaction()方法先提交部分事務,給其他事務執行的機會。

3.2.2 SQLiteConnectionPool

  資料庫連接池保持所有開啟的資料庫連接,在任何時候,一個資料庫連接要麼被串連池持有,要麼被一個SQLiteSession持有,如果SQLiteSession使用完資料庫連接,必須把它還給串連池。如果串連池中所有的串連都已被佔用,則待執行的事務要等待有閒置串連才能執行。

  目前Android系統的實現中,如果以非WAL模式開啟資料庫,串連池中只會保持一個資料庫連接,如果以WAL模式開啟資料庫,串連池中的最大串連數量則根據系統配置決定,預設配置是兩個。

SQLiteConnectionPool.java:

  雖然名為串連池,但是從源碼來看,目前實現的池中只有一個資料庫連接(以後的Android版本可能會擴充),所以如果應用程式中有大量的並發資料庫讀和寫操作的話,每個操作的時間長度都可能受到影響,所以資料庫操作應放在背景工作執行緒中執行,以免影響UI響應。

3.2.4 Cursor

  從ContentProvider查詢的資料結果是放在Cursor中返回給用戶端的,在用戶端看來Cursor就是一個資料容器,但隱藏在Cursor後面的實現方式很靈活,它的資料既可以不是從資料庫返回的,也可以是在使用時才真正載入的,很好的體現了物件導向編程的特性和優點。

  在Frameworks中把Cursor定義為了一個介面,它的定位是可以隨機訪問的資料集,在介面中定義了訪問資料集的通用方法。業務可以根據自己的需要實現一個Cursor,具體怎麼實現介面的方法由具體實現決定,所以Cursor有很多子類來滿足不同情境的需要。

  通過SQLiteDatabase返回的就是其中的SQLiteCursor,如果資料不是從資料庫返回的,開發人員也可以在ContentProvider中動態建立一個MatrixCursor,然後填充資料並返回給用戶端。

  從Cursor介面和其衍生類別的定義來看它們都沒有實現Parcelable介面,那麼它是怎麼跨進程傳遞的呢?這需要Cursor首先要解決兩個問題。

  第一個問題:Cursor沒有實現Parcelable介面,一個Cursor執行個體怎麼跨進稱傳遞呢?答案是傳遞的不是具體資料,而是Binder引用,即在ContentProvider端建立Cursor的Binder服務端執行個體,然後把Binder應用傳遞給用戶端,在用戶端通過這個Binder引用跨進程擷取查詢到的資料的。這裡Frameworks定義了一個介面:IBulkCursor。

  IBulkCursor定義了跨進程的Cursor需要實現的介面方法,其中getWindow()用來獲得資料視窗,onMove()用來移動Cursor的位置。

  那麼第二個問來了:我們知道通過Binder傳遞的資料大小有限(1MB),而查詢到的資料大小可能超出限制,那麼怎麼跨進程傳遞資料呢?既然資料大小不定,那麼我們就不通過Binder傳遞資料了,而是通過共用記憶體傳遞資料,這塊共用記憶體是封裝在CursorWindow中的。

  CursorWindow就是資料視窗,它在服務端分配(視窗大小有Android系統配置決定)並傳遞到用戶端,用戶端再映射到自己的進程空間中,這樣,服務端填充的資料就可以被用戶端讀取到了。上面IBulkCursor介面中定義的getWindow()方法就是擷取CursorWindow的。

  CursorWindow在初始化時是空的,在調用Cursor的moveToXXX方法時會通過IBulkCursor的onMove()方法調用服務端的Cursor去填充資料視窗的內容。

CursorWindow.java

frameworks\base\libs\androidfw\CursorWindow.cpp

  服務端建立共用記憶體。

CursorWindow.java

frameworks\base\libs\androidfw\CursorWindow.cpp

  用戶端映射共用記憶體到進程記憶體空間。

  上面知道了Frameworks解決跨進程傳遞Cursor資料的思路,我們再來看下具體執行跨進程傳遞資料的類:CursorToBulkCursorAdapter(服務端)和BulkCursorToCursorAdapter(用戶端)。

服務端:

用戶端:

  在ContentProviderNatvie類中可以看到Cursor的專遞過程。

服務端:

ContentProviderNative.onTransact()

CursorToBulkCursorAdaptor.java

  服務端在通過ContentProvider得到Cursor後,用它建立一個CursorToBulkCursorAdaptor執行個體,然後把adaptor封裝在一個實現了Parcelable介面的BulkCursorDescriptor執行個體中返回給用戶端。

  雖說是在使用時才填充資料視窗,但是實際上傳遞Cursor的過程中,從上面代碼可以看到服務端已經替應用程式填充過一次資料了:mCursor.getCount()。

SQLiteCursor.java

用戶端:

ContentProviderProxy.query()

BulkCursorToCursorAdaptor.java 

    在得到服務端返回的資料後建立一個BulkCursorDescriptor執行個體,在用它初始化一個BulkCursorToCursorAdapter執行個體返回給應用程式使用。 

SQLite資料庫學習小結(2)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.