以下資料摘錄整理自老羅的Android之旅博客,是對老羅的博客關於Android底層原理的一個抽象的知識概括總結(如有錯誤歡迎指出)(侵刪):
http://blog.csdn.net/luoshengyang/article/details/8923485
http://blog.csdn.net/luoshengyang/article/details/12957169
整理by Doing
安裝應用程序的時候,並不會把相應的Content Provider加載到內存中來,系統采取的是懶加載的機制,等到第一次要使用這個Content Provider的時候,系統才會把它加載到內存中來,下次再要使用這個Content Provider的時候,就可以直接返回
Android應用程序組件Content Provider在應用程序之間共享數據的原理
Content Provider組件在不同應用程序之間傳輸數據是基於
匿名共享內存機制來實現的(在應用程序進程之間以匿名共享內存的方式來傳輸數據效率是非常高的,因為它們之間只需要傳遞一個文件描述符就可以了):
首先
在第三方應用程序這一側,當它需要訪問Content Provider中的數據時,它會在本進程中
創建一個CursorWindow對象,它在內部創建了一塊匿名共享內存,同時,它實現了Parcel接口,因此它可以在進程間傳輸。接下來第三方應用程序
把這個CursorWindow對象(連同它內部的匿名共享內存文件描述符)通過Binder進程間調用傳輸到Content Provider這一側。這個匿名共享內存文件描述符傳輸到Binder驅動程序的時候,Binder驅動程序就會在目標進程(即Content Provider所在的進程)中
創建另一個匿名共享文件描述符,指向前面已經創建好的匿名共享內存,因此,就實現了在
兩個進程中共享同一塊匿名內存。
在
Content Provider這一側,
利用在Binder驅動程序為它創建好的這個匿名共享內存文件描述符,在本進程中創建了一個CursorWindow對象。現在,Content Provider開始要從本地中從數據庫中查詢第三方應用程序想要獲取的數據了。Content Provider首先會創建一個
SQLiteCursor對象,即SQLite數據庫游標對象,它繼承了AbstractWindowedCursor類,后者又繼承了AbstractCursor類,而AbstractCursor類又實現了CrossProcessCursor和Cursor接口。其中,最重要的是在AbstractWindowedCursor類中,有一個成員變量mWindow,它的類型為CursorWindow,這個成員變量是通過AbstractWindowedCursor的子類SQLiteCursor的setWindow成員函數來設置的。這個SQLiteCursor對象設置好了父類AbstractWindowedCursor類的mWindow成員變量之后,它就具有傳輸數據的能力了,因為這個mWindow對象內部包含一塊匿名共享內存。此外,這個SQLiteCursor對象的內部有兩個成員變量,一個是SQLite數據庫對象mDatabase,另外一個是SQLite數據庫查詢對象mQuery。SQLite數據庫查詢對象mQuery的類型為SQLiteQuery,它繼承了SQLiteProgram類,后者又繼承了SQLiteClosable類。SQLiteProgram類代表一個數據庫存查詢計划,它的成員變量mCompiledSql包含了一個已經編譯好的SQL查詢語句,
SQLiteCursor對象就是利用這個編譯好的SQL查詢語句來獲得數據的,但是它並不是馬上就去獲取數據的,而是等到需要時才去獲取。
那么,要等到什么時候才會需要獲取數據呢?一般來說,如果第三方應用程序在請求Content Provider返回數據時,如果指定了要返回關於這些數據的元信息時,例如數據條目的數量,那么Content Provider在把這個SQLiteCursor對象返回給第三方應用程序之前,就會去獲取數據,因為只有獲取了數據之后,才知道數據條目的數量是多少。SQLiteCursor對象通過調用成員變量mQuery的fillWindow成員函數來把從SQLite數據庫中查詢得到的數據保存其父類AbstractWindowedCursor的成員變量mWindow中去,即保存到第三方應用程序創建的這塊匿名共享內存中去。如果第三方應用程序在請求Content Provider返回數據時,沒有指定要返回關於這些數據的元信息,那么,就要等到第三方應用程序首次調用這個從Content Provider處返回的SQLiteCursor對象的數據獲取方法時,才會真正執行從數據庫存中查詢數據的操作,例如調用了SQLiteCursor對象的getCount或者moveToFirst成員函數時。這是一種數據懶加載機制,需要的時候才去加載,這樣就提高了數據傳輸過程中的效率。
上面說到,
Content Provider向第三方應用程序返回的數據實際上是一個SQLiteCursor對象,那么,這個SQLiteCursor對象是如何傳輸到第三方應用程序的呢?因為它本身並不是一個Binder對象,我們需要對它進行適配一下。首先,Content Provider會根據這個SQLiteCursor對象來創建一個CursorToBulkCursorAdaptor適配器對象,這個適配器對象是一個Binder對象,因此,它可以在進程間傳輸,同時,它實現了IBulkCursor接口。Content Provider接着就通過Binder進程間通信機制把這個CursorToBulkCursorAdaptor對象返回給第三方應用程序,第三方應用程序得到了這個CursorToBulkCursorAdaptor之后,再在本地創建一個BulkCursorToCursorAdaptor對象,這個BulkCursorToCursorAdaptor對象的繼承結構和SQLiteCursor對象是一樣的,不過,它沒有設置父類AbstractWindowedCursor的mWindow成員變量,因此,它只可以通過它內部的CursorToBulkCursorAdaptor對象引用來訪問匿名共享內存中的數據,即通過訪問Content Provider這一側的SQLiteCursor對象來訪問共享數據。
Content Provider的共享數據更新通知機制:
ContentObserver類的成員變量mTransport是一個Binder對象,它是要傳遞給ContentService服務的,以便當ContentObserver所監控的數據發生變化時,ContentService服務可以通過這個Binder對象通知相應的ContentObserver它監控的數據發生變化了
Android應用程序組件Content Provider中的數據更新通知機制和Android系統中的廣播(Broadcast)通知機制的實現思路是相似的。然而,Content Provider中的數據監控機制與Android系統中的廣播機制又有三個主要的區別:
- 一是前者是通過URI來把通知的發送者和接收者關聯在一起的,而后者是通過Intent來關聯的
- 二是前者的通知注冊中心是由ContentService服務來扮演的,而后者是由ActivityManagerService服務來扮演的
- 三是前者負責接收數據更新通知的類必須要繼承ContentObserver類,而后者要繼承BroadcastReceiver類。
之所以會有這些區別,是由於Content Proivder組件的數據共享功能本身就是建立在URI的基礎之上的,因此專門針對URI來設計另外一套通知機制會更實用和方便,而Android系統的廣播機制是一種更加通用的事件通知機制,它的適用范圍會更廣泛一些。