很好的一片文章,我就轉載了,學習一下。
本文出自:http://blog.sina.com.cn/s/blog_49f62c350101hhhl.html
一.Android四大組件
Android四大組件是Activity, Service, Content Provider,Broadcast Receiver。
Activity作爲程序界面,直接與用戶交互
Service運行在後臺,沒有界面,完成特定的功能
ContentProvider維護應用數據,方便應用本身或其它應用訪問
Broadcast Receiver提供異步廣播消息接收機制,便於各應用/組件進行交互
通過AndroidManifest.xml, 可以看到一個應用使用了哪些組件:
attribute的定義可以參考http://developer.android.com/guide/topics/manifest/manifest-intro.html
下面重點探討Content Provider的實現和使用。
二.什麼是ContentProvider
Content Provider維護特定的應用數據,並可以讓其它應用輕鬆訪問該數據。對數據使
用者來說它是數據提供者。它提供統一的接口對數據進行操作,使用者不用關心數據到底是如何存儲的以及數據類型到底是什麼。也就是說,ContentProvider作爲數據提供者,提供了對外共享本地數據一種機制,使Android應用能方便地基於該機制進行數據訪問。
爲了便於管理和訪問,每個ContentProvider必須有唯一標示,用Uri表示。Uri類似http url, 構成如下:
content://authority/path
所有Content Provider的Uri必須以content://開頭,這是Android規定的。
authority是個字符串,它由開發者自己定義,用於來唯一標示一個ContentProvider。系統會根據這個標示查找ContentProvider。
path也是字符串,表示要操作的數據。可根據自己的實現邏輯來指定:
content://contacts/people表示要操作ContentProvider爲contacts下的people表
content://com.android.contacts/people/#表示要操作表people中特定id的行(記錄)。
content://downloads/download/10/name表示要操作id爲10的行的name字段。
content://downloads/download/*表示操作download表中的所有字段。
總之,#匹配一個數字字符串,*匹配一個文本字符串。
三.ContentProvider 的實現和使用
可以看出, 實現一個自定義的ContentProvider,要基於系統提供的基類ContentProvider,需要實現6個接口。大部分接口就是類似數據庫的數據操作接口,實際上ContentProvider是需要創建數據庫並對數據庫進行操作的。完成實現之後,在Androidmanifest.xml中聲明自己的ContentProvider以及與Provider相關的permission聲明(可以沒有permission定義)。例如:
最後整個應用被編譯成apk。安裝之後,該應用裏的contentProvider就可以被其它應用訪問了。對於Provider使用者來說, 如果特定Provider有permission要求,則要在自己的Androidmanifest.xml中添加指定Permission引用,
如:
使用非常簡單,Android提供了Context級別的ContentResolver對象來對ContentProvider進行操作。正是因爲有了ContentResolver, 使用者纔不用關心Provider到底是哪個應用或哪個類實現的。只要知道它的uri就能訪問。ContentResolver對象存在於每個Context中。幾乎所有對象都有自己的Context。
有些情況下,Content Provider使用者想監聽數據的變化,可以註冊一個Observer:
四.ContentProvider內部機制
1.ContentProvider接口調用過程
ContentProvider依賴ContentResolver/ActivityThread/ActivityManagerService對外提供
服務。雖然ContentProvider的用法以及表現形式不是一個Service,實際上它可以看作是ActivityManagerService提供的一種服務,它實現了IBinder接口。
首先調用者通過特定uri調用特定ContentProvider的接口函數,比如insert(), 此時ContentResolver會通過uri獲取特定ContentProvider的實例,ActivityThread檢查本地Cache,如果發現此ContentProvider已經被引用過,則直接直接取出ContentProvider返回給調用者。如果沒有發現,由於ContentProvider可能已經被load了,可能還沒有load;可能要創建Process,可能要檢查permission,所以ActivityThread調用到ActivityManagerService來進行相關處理/檢查。如果該Provider是Single Process,ActivityManagerService會爲ContentProvider創建一個獨立Process;如果是MultiProcess,說明每個調用者可以擁有獨立的ContentProvider實例,於是ActivityManagerService只是返回ContentProvider的相關信息給ActivityThread,由ActivityThread負責ContentProvider的實例化,此時ContentProvider運行在調用者Process中。實例化後,IConentProvider會返回給調用者,通過該接口可以調用所需功能。
ActivityThread本地維護一個mProviderMap<ProviderName, ProviderRecord>,記錄已被引用的ContentProvider, 同時使用引用計數mProviderRefCountMap<IBinder,ProviderRefCount>記錄特定ContentProvider的引用情況。
2.ContentProvider實例創建過程
ContentProvider實例的創建與multiprocess屬性有關係(Androidmanifest.xml裏指定),個人認爲理解成多進程並不準確。應該理解爲ContentProvider的多實例,不會存在多個ContentProvider進程的情況,ContentProvider可能存在多個實例。
1)對於android:multiprocess=true的ContentProvider,意味着可以多實例,那麼由調用者在自己的進程空間實例化一個ContentProvider對象,此時定義ContentProvider的App可能並沒有啓動
注意:ContentProvider是否多實例,還得看contentProvider的uid與調用者的uid是否相同或contentProvider的uid是System user。具體邏輯是:
public boolean canRunHere(ProcessRecord app) {
return (info.multiprocess ||info.processName.equals(app.processName))
&& (uid == Process.SYSTEM_UID ||uid == app.info.uid);
}
2)對於android:multiprocess=false(默認值)的ContentProvider,由系統把定義該ContentProvider的App啓動起來(一個獨立的Process)並實例化ContentProvider,這種ContentProvider只有一個實例,運行在自己App的Process中。所有調用者共享該ContentProvider實例,調用者與ContentProvider實例位於兩個不同的Process
其中Process.start()->zygoteSendArgsAndGetPid()->ZygoteInit.runSelectLoopMode()
-> ZygoteConnection.runOnce() ->Zygote.forkAndSpecialize()->RuntimeInit.zygoteInit()
->invokeStaticMain()->MethodAndArgsCaller.run()->Method.invokeNative
->ActivityThread.main(),這是通用的APK啓動流程。
3)ContentProvider的加載/發佈過程
ContentProvider不能單獨發佈,總是被打包到某個Android應用(apk)裏。APK被安裝之後,實例化之後每個Android應用都有一個Application實例,每個Activity或Service有一個ApplicationContext實例。
Android應用程序的入口函數是ActivityThread.main(),該函數不僅創建了ActivityThread實例以及消息循環機構,而且創建了ApplicationThread實例,通過此實例向ActivityManager Service(AMS)提供IApplicationThread接口,AMS正是通過該接口調度和管理Activity。
ActivityThread通過attachApplication()把自己的ApplicationThread實例告知AMS。
AMS根據thread信息更新進程記錄(ProcessRecord)並調用thread的bindApplication()進行初始化工作並創建ApplicationContext和Application實例,然後安裝package裏聲明的所有contentProvider。 主要過程如下:
AMS維護了很多信息,其中比較重要的有:
mProcessNames: 包名(processName)和進程信息(ProcessRecord)映射表
mProvidersByName: Provider發佈名和Provider信息(ContentProviderRecord)映射表
mProvidersByClass: Provider類名和Provider信息(ContentProviderRecord)映射表
conProviders:屬於ProcessRecord信息,特定Process正在使用的ContentProvider及其個數映射表
pubProviders:屬於ProcessRecord信息,特定Process已經Published的Provider類名和Provider信息
(ContentProviderRecord)映射表
ActivityThread維護了3個與ContentProvider相關的Map:
mProviderMap: 記錄本應用已使用的Provider信息:<Provider發佈名, ProviderRecord>
mProviderRefCountMap: 記錄本應用已使用的Provider引用計數信息:<IBinder,ProviderRefCount>
mLocalProviders: 記錄本地加載的Provider信息:<IBinder,ProviderRecord>
4)ContentProvider通知機制
注意:這個通知機制需要ContentProvider的實現者在實現insert/delete/query/update接口時調用ContentResolver的notifyChange(),否則沒法實現數據變化的通知。
參考代碼:
\frameworks\base\core\java\android\app\ActivityThread.java
\frameworks\base\core\java\android\content\ContentResolver.java
\frameworks\base\core\java\android\content\ContentContent.java
\frameworks\base\core\java\android\content\ContentService.java
\frameworks\base\core\java\android\database\ContentObserver.java
\frameworks\base\services\java\com\android\server\am\ActivityManagerService.java