nativePollOnce的實現函數是android_os_MessageQueue_nativePollOnce,代碼如下:
**android_os_MessageQueue.cpp**
~~~
static void android_os_MessageQueue_nativePollOnce(JNIEnv*env, jobject obj,
jintptr, jint timeoutMillis)
NativeMessageQueue*nativeMessageQueue =
reinterpret_cast<NativeMessageQueue*>(ptr);
//取出NativeMessageQueue對象,并調用它的pollOnce
nativeMessageQueue->pollOnce(timeoutMillis);
}
//分析pollOnce函數
void NativeMessageQueue::pollOnce(inttimeoutMillis) {
mLooper->pollOnce(timeoutMillis); //重任傳遞到Looper的pollOnce函數
}
~~~
Looper的pollOnce函數如下:
**Looper.cpp**
~~~
inline int pollOnce(int timeoutMillis) {
return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
~~~
上面的函數將調用另外一個有4個參數的pollOnce函數,這個函數的原型如下:
int pollOnce(int timeoutMillis, int* outFd, int*outEvents, void** outData)
其中:
- timeOutMillis參數為超時等待時間。如果為-1,則表示無限等待,直到有事件發生為止。如果值為0,則無需等待立即返回。
- outFd用來存儲發生事件的那個文件描述符。
- outEvents用來存儲在該文件描述符[[^write]上發生了哪些事件,目前支持可讀、可寫、錯誤和中斷4個事件。這4個事件其實是從epoll事件轉化而來。后面我們會介紹大名鼎鼎的epoll。
- outData用于存儲上下文數據,這個上下文數據是由用戶在添加監聽句柄時傳遞的,它的作用和pthread_create函數最后一個參數param一樣,用來傳遞用戶自定義的數據。
另外,pollOnce函數的返回值也具有特殊的意義,具體如下:
- 當返回值為ALOOPER_POLL_WAKE時,表示這次返回是由wake函數觸發的,也就是管道寫端的那次寫事件觸發的。
- 返回值為ALOOPER_POLL_TIMEOUT表示等待超時。
- 返回值為ALOOPER_POLL_ERROR,表示等待過程中發生錯誤。
返回值為ALOOPER_POLL_CALLBACK,表示某個被監聽的句柄因某種原因被觸發。這時,outFd參數用于存儲發生事件的文件句柄,outEvents用于存儲所發生的事件。
上面這些知識是和epoll息息相關的。
* * * * *
**提示**查看Looper的代碼會發現,Looper采用了編譯選項(即#if和#else)來控制是否使用epoll作為I/O復用的控制中樞。鑒于現在大多數系統都支持epoll,這里僅討論使用epoll的情況。
* * * * *
1. epoll基礎知識介紹
epoll機制提供了Linux平臺上最高效的I/O復用機制,因此有必要介紹一下它的基礎知識。
從調用方法上看,epoll的用法和select/poll非常類似,其主要作用就是I/O復用,即在一個地方等待多個文件句柄的I/O事件。
下面通過一個簡單例子來分析epoll的工作流程。
**epoll工作流程分析案例**
~~~
/*
使用epoll前,需要先通過epoll_create函數創建一個epoll句柄。
下面一行代碼中的10表示該epoll句柄初次創建時候分配能容納10個fd相關信息的緩存。
對于2.6.8版本以后的內核,該值沒有實際作用,這里可以忽略。其實這個值的主要目的是
確定分配一塊多大的緩存。現在的內核都支持動態拓展這塊緩存,所以該值就沒有意義了
*/
int epollHandle = epoll_create(10);
/*
得到epoll句柄后,下一步就是通過epoll_ctl把需要監聽的文件句柄加入到epoll句柄中。
除了指定文件句柄本身的fd值外,同時還需要指定在該fd上等待什么事件。epoll支持四類事件,
分別是EPOLLIN(句柄可讀)、EPOLLOUT(句柄可寫),EPOLLERR(句柄錯誤)、EPOLLHUP(句柄斷)。
epoll定義了一個結構體struct epoll_event來表達監聽句柄的訴求。
假設現在有一個監聽端的socket句柄listener,要把它加入到epoll句柄中。
*/
structepoll_event listenEvent; //先定義一個event
/*
EPOLLIN表示可讀事件,EPOLLOUT表示可寫事件,另外還有EPOLLERR,EPOLLHUP表示
系統默認會將EPOLLERR加入到事件集合中
*/
listenEvent.events= EPOLLIN;//指定該句柄的可讀事件
//epoll_event中有一個聯合體叫data,用來存儲上下文數據,本例的上下文數據就是句柄自己
listenEvent.data.fd= listenEvent;
/*
EPOLL_CTL_ADD將監聽fd和監聽事件加入到epoll句柄的等待隊列中;
EPOLL_CTL_DEL將監聽fd從epoll句柄中移除;
EPOLL_CTL_MOD修改監聽fd的監聽事件,例如本來只等待可讀事件,現在需要同時等待
可寫事件,那么修改listenEvent.events 為EPOLLIN|EPOLLOUT后,再傳給epoll句柄
*/
epoll_ctl(epollHandle,EPOLL_CTL_ADD,listener,&listenEvent);
/*
當把所有感興趣的fd都加入到epoll句柄后,就可以開始坐等感興趣的事情發生了。
為了接收所發生的事情,先定義一個epoll_event數組
*/
struct epoll_eventresultEvents[10];
inttimeout = -1;
while(1)
{
/*
調用epoll_wait用于等待事件,其中timeout可以指定一個超時時間,
resultEvents用于接收發生的事件,10為該數組的大小。
epoll_wait函數的返回值有如下含義:
nfds大于0表示所監聽的句柄上有事件發生;
nfds等于0表示等待超時;
nfds小于0表示等待過程中發生了錯誤
*/
int nfds= epoll_wait(epollHandle, resultEvents, 10, timeout);
if(nfds== -1)
{
// epoll_wait發生了錯誤
}
elseif(nfds == 0)
{
//發生超時,期間沒有發生任何事件
}
else
{
//resultEvents用于返回那些發生了事件的信息
for(int i = 0; i < nfds; i++)
{
struct epoll_event & event =resultEvents[i];
if(event & EPOLLIN)
{
/*
收到可讀事件。到底是哪個文件句柄發生該事件呢?可通過event.data這個聯合體取得
之前傳遞給epoll的上下文數據,該上下文信息可用于判斷到底是誰發生了事件。
*/
}
.......//其他處理
}
}
}
~~~
epoll整體使用流程如上面代碼所示,基本和select/poll類似,不過作為Linux平臺最高效的I/O復用機制,這里有些內容供讀者參考,
epoll的效率為什么會比select高?其中一個原因是調用方法。每次調用select時,都需要把感興趣的事件復制到內核中,而epoll只在epll_ctl進行加入的時候復制一次。另外,epoll內部用于保存事件的數據結構使用的是紅黑樹,查找速度很快。而select采用數組保存信息,不但一次能等待的句柄個數有限,并且查找起來速度很慢。當然,在只等待少量文件句柄時,select和epoll效率相差不是很多,但筆者還是推薦使用epoll。
epoll等待的事件有兩種觸發條件,一個是水平觸發(EPOLLLEVEL),另外一個是邊緣觸發(EPOLLET,ET為Edge Trigger之意),這兩種觸發條件的區別非常重要。讀者可通過man epoll查閱系統提供的更為詳細的epoll機制。
最后,關于pipe,還想提出一個小問題供讀者思考討論:
為什么Android中使用pipe作為線程間通訊的方式?對于pipe的寫端寫入的數據,讀端都不感興趣,只是為了簡單的喚醒。POSIX不是也有線程間同步函數嗎?為什么要用pipe呢?
關于這個問題的答案,可參見筆者一篇博文“隨筆之如何實現一個線程池”。
2. pollOnce函數分析
下面分析帶4個參數的pollOnce函數,代碼如下:
**Looper.cpp**
~~~
int Looper::pollOnce(int timeoutMillis, int*outFd, int* outEvents,
void** outData) {
intresult = 0;
for (;;){ //一個無限循環
//mResponses是一個Vector,這里首先需要處理response
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
ALooper_callbackFunc callback = response.request.callback;
if (!callback) {//首先處理那些沒有callback的Response
int ident = response.request.ident; //ident是這個Response的id
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
......
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
//實際上,對于沒有callback的Response,pollOnce只是返回它的
//ident,并沒有實際做什么處理。因為沒有callback,所以系統也不知道如何處理
return ident;
}
}
if(result != 0) {
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = NULL;
if (outData != NULL) *outData = NULL;
return result;
}
//調用pollInner函數。注意,它在for循環內部
result = pollInner(timeoutMillis);
}
}
~~~
初看上面的代碼,可能會讓人有些丈二和尚摸不著頭腦。但是把pollInner函數分析完畢,大家就會明白很多。pollInner函數非常長,把用于調試和統計的代碼去掉,結果如下:
**Looper.cpp**
~~~
int Looper::pollInner(int timeoutMillis) {
if(timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
......//根據Native Message的信息計算此次需要等待的時間
timeoutMillis= messageTimeoutMillis;
}
intresult = ALOOPER_POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
#ifdef LOOPER_USES_EPOLL //我們只討論使用epoll進行I/O復用的方式
structepoll_event eventItems[EPOLL_MAX_EVENTS];
//調用epoll_wait,等待感興趣的事件或超時發生
inteventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS,
timeoutMillis);
#else
......//使用別的方式進行I/O復用
#endif
//從epoll_wait返回,這時候一定發生了什么事情
mLock.lock();
if(eventCount < 0) { //返回值小于零,表示發生錯誤
if(errno == EINTR) {
goto Done;
}
//設置result為ALLOPER_POLL_ERROR,并跳轉到Done
result = ALOOPER_POLL_ERROR;
gotoDone;
}
//eventCount為零,表示發生超時,因此直接跳轉到Done
if(eventCount == 0) {
result = ALOOPER_POLL_TIMEOUT;
gotoDone;
}
#ifdef LOOPER_USES_EPOLL
//根據epoll的用法,此時的eventCount表示發生事件的個數
for (inti = 0; i < eventCount; i++) {
intfd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
/*
之前通過pipe函數創建過兩個fd,這里根據fd知道是管道讀端有可讀事件。
讀者還記得對nativeWake函數的分析嗎?在那里我們向管道寫端寫了一個”W”字符,這樣
就能觸發管道讀端從epoll_wait函數返回了
*/
if(fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
//awoken函數直接讀取并清空管道數據,讀者可自行研究該函數
awoken();
}
......
}else {
/*
mRequests和前面的mResponse相對應,它也是一個KeyedVector,其中存儲了
fd和對應的Request結構體,該結構體封裝了和監控文件句柄相關的一些上下文信息,
例如回調函數等。我們在后面的小節會再次介紹該結構體
*/
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
//將epoll返回的事件轉換成上層LOOPER使用的事件
if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
//每處理一個Request,就相應構造一個Response
pushResponse(events, mRequests.valueAt(requestIndex));
}
......
}
}
Done: ;
#else
......
#endif
//除了處理Request外,還處理Native的Message
mNextMessageUptime = LLONG_MAX;
while(mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope =mMessageEnvelopes.itemAt(0);
if(messageEnvelope.uptime <= now) {
{
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
//調用Native的handler處理Native的Message
//從這里也可看出NativeMessage和Java層的Message沒有什么關系
handler->handleMessage(message);
}
mLock.lock();
mSendingMessage = false;
result = ALOOPER_POLL_CALLBACK;
}else {
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
mLock.unlock();
//處理那些帶回調函數的Response
for(size_t i = 0; i < mResponses.size(); i++) {
const Response& response = mResponses.itemAt(i);
ALooper_callbackFunc callback = response.request.callback;
if(callback) {//有了回調函數,就能知道如何處理所發生的事情了
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
//調用回調函數處理所發生的事件
int callbackResult = callback(fd, events, data);
if (callbackResult == 0) {
//callback函數的返回值很重要,如果為0,表明不需要再次監視該文件句柄
removeFd(fd);
}
result = ALOOPER_POLL_CALLBACK;
}
}
returnresult;
}
~~~
看完代碼了,是否還有點模糊?那么,回顧一下pollInner函數的幾個關鍵點:
- 首先需要計算一下真正需要等待的時間。
- 調用epoll_wait函數等待。
- epoll_wait函數返回,這時候可能有三種情況:
- 發生錯誤,則跳轉到Done處。
- 超時,這時候也跳轉到Done處。
- epoll_wait監測到某些文件句柄上有事件發生。
- 假設epoll_wait因為文件句柄有事件而返回,此時需要根據文件句柄來分別處理:
- 如果是管道讀這一端有事情,則認為是控制命令,可以直接讀取管道中的數據。
- 如果是其他FD發生事件,則根據Request構造Response,并push到Response數組中。
- 真正開始處理事件是在有Done標志的位置。
- 首先處理Native的Message。調用Native Handler的handleMessage處理該Message。
- 處理Response數組中那些帶有callback的事件。
上面的處理流程還是比較清晰的,但還是有個一個攔路虎,那就是mRequests,下面就來清剿這個攔路虎。
3. 添加監控請求
添加監控請求其實就是調用epoll_ctl增加文件句柄。下面通過從Native的Activity找到的一個例子來分析mRequests。
**android_app_NativeActivity.cpp**
~~~
static jint
loadNativeCode_native(JNIEnv* env, jobject clazz,jstring path,
jstring funcName,jobject messageQueue,
jstring internalDataDir, jstring obbDir,
jstring externalDataDir, int sdkVersion,
jobject jAssetMgr, jbyteArraysavedState)
{
......
/*
調用Looper的addFd函數。第一個參數表示監聽的fd;第二個參數0表示ident;
第三個參數表示需要監聽的事件,這里為只監聽可讀事件;第四個參數為回調函數,當該fd發生
指定事件時,looper將回調該函數;第五個參數code為回調函數的參數
*/
code->looper->addFd(code->mainWorkRead,0,
ALOOPER_EVENT_INPUT,mainWorkCallback, code);
......
}
~~~
Looper的addFd代碼如下所示:
**Looper.cpp**
~~~
int Looper::addFd(int fd, int ident, int events,
ALooper_callbackFunccallback, void* data) {
if (!callback) {
//判斷該Looper是否支持不帶回調函數的文件句柄添加。一般不支持,因為沒有回調函數
//Looper也不知道如何處理該文件句柄上發生的事情
if(! mAllowNonCallbacks) {
return -1;
}
......
}
#ifdefLOOPER_USES_EPOLL
intepollEvents = 0;
//將用戶的事件轉換成epoll使用的值
if(events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
if(events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
{
AutoMutex _l(mLock);
Request request; //創建一個Request對象
request.fd = fd; //保存fd
request.ident = ident; //保存id
request.callback = callback; //保存callback
request.data = data; //保存用戶自定義數據
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event));
eventItem.events = epollEvents;
eventItem.data.fd = fd;
//判斷該Request是否已經存在,mRequests以fd作為key值
ssize_t requestIndex = mRequests.indexOfKey(fd);
if(requestIndex < 0) {
//如果是新的文件句柄,則需要為epoll增加該fd
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
......
//保存Request到mRequests鍵值數組
mRequests.add(fd, request);
}else {
//如果之前加過,那么就修改該監聽句柄的一些信息
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, &eventItem);
......
mRequests.replaceValueAt(requestIndex, request);
}
}
#else
......
#endif
return1;
}
~~~
4. 處理監控請求
我們發現在pollInner函數中,當某個監控fd上發生事件后,就會把對應的Request取出來調用。
pushResponse(events, mRequests.itemAt(i));
此函數如下:
**Looper.cpp**
~~~
void Looper::pushResponse(int events, constRequest& request) {
Responseresponse;
response.events = events;
response.request = request; //其實很簡單,就是保存所發生的事情和對應的Request
mResponses.push(response); //然后保存到mResponse數組
}
~~~
根據前面的知識可知,并不是單獨處理Request,而是需要先收集Request,等到Native Message消息處理完之后再做處理。這表明,在處理邏輯上,Native Message的優先級高于監控FD的優先級。
下面我們來了解如何添加Native的Message。
5. Native的sendMessage
Android 2.2中只有Java層才可以通過sendMessage往MessageQueue中添加消息,從4.0開始,Native層也支持sendMessage了[^platform]。sendMessage的代碼如下:
**Looper.cpp**
~~~
void Looper::sendMessage(constsp<MessageHandler>& handler,
constMessage& message) {
//Native的sendMessage函數必須同時傳遞一個Handler
nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now, handler, message); //調用sendMessageAtTime
}
void Looper::sendMessageAtTime(nsecs_t uptime,
const sp<MessageHandler>& handler,
const Message& message) {
size_t i= 0;
{ //acquire lock
AutoMutex _l(mLock);
size_t messageCount = mMessageEnvelopes.size();
//按時間排序,將消息插入到正確的位置上
while (i < messageCount &&
uptime >= mMessageEnvelopes.itemAt(i).uptime) {
i += 1;
}
MessageEnvelope messageEnvelope(uptime, handler, message);
mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
//mSendingMessage和Java層中的那個mBlocked一樣,是一個小小的優化措施
if(mSendingMessage) {
return;
}
}
//喚醒epoll_wait,讓它處理消息
if (i ==0) {
wake();
}
}
~~~
[^write]: 注意,以后文件描述符也會簡寫為文件句柄。
[^platform]: 我們這里略過了Android2.2到Android 4.0之間幾個版本中的代碼變化。
- 前言
- 第1章 搭建Android源碼工作環境
- 1.1 Android系統架構
- 1.2 搭建開發環境
- 1.2.1 下載源碼
- 1.2.2 編譯源碼
- 1.2.3 利用Eclipse調試system_process
- 1.3 本章小結
- 第2章 深入理解Java Binder和MessageQueue
- 2.1 概述
- 2.2 Java層中的Binder架構分析
- 2.2.1 Binder架構總覽
- 2.2.2 初始化Java層Binder框架
- 2.2.3 addService實例分析
- 2.2.4 Java層Binder架構總結
- 2.3 心系兩界的MessageQueue
- 2.3.1 MessageQueue的創建
- 2.3.2 提取消息
- 2.3.3 nativePollOnce函數分析
- 2.3.4 MessageQueue總結
- 2.4 本章小結
- 第3章 深入理解SystemServer
- 3.1 概述
- 3.2 SystemServer分析
- 3.2.1 main函數分析
- 3.2.2 Service群英會
- 3.3 EntropyService分析
- 3.4 DropBoxManagerService分析
- 3.4.1 DBMS構造函數分析
- 3.4.2 dropbox日志文件的添加
- 3.4.3 DBMS和settings數據庫
- 3.5 DiskStatsService和DeviceStorageMonitorService分析
- 3.5.1 DiskStatsService分析
- 3.5.2 DeviceStorageManagerService分析
- 3.6 SamplingProfilerService分析
- 3.6.1 SamplingProfilerService構造函數分析
- 3.6.2 SamplingProfilerIntegration分析
- 3.7 ClipboardService分析
- 3.7.1 復制數據到剪貼板
- 3.7.2 從剪切板粘貼數據
- 3.7.3 CBS中的權限管理
- 3.8 本章小結
- 第4章 深入理解PackageManagerService
- 4.1 概述
- 4.2 初識PackageManagerService
- 4.3 PKMS的main函數分析
- 4.3.1 構造函數分析之前期準備工作
- 4.3.2 構造函數分析之掃描Package
- 4.3.3 構造函數分析之掃尾工作
- 4.3.4 PKMS構造函數總結
- 4.4 APK Installation分析
- 4.4.1 adb install分析
- 4.4.2 pm分析
- 4.4.3 installPackageWithVerification函數分析
- 4.4.4 APK 安裝流程總結
- 4.4.5 Verification介紹
- 4.5 queryIntentActivities分析
- 4.5.1 Intent及IntentFilter介紹
- 4.5.2 Activity信息的管理
- 4.5.3 Intent 匹配查詢分析
- 4.5.4 queryIntentActivities總結
- 4.6 installd及UserManager介紹
- 4.6.1 installd介紹
- 4.6.2 UserManager介紹
- 4.7 本章學習指導
- 4.8 本章小結
- 第5章 深入理解PowerManagerService
- 5.1 概述
- 5.2 初識PowerManagerService
- 5.2.1 PMS構造函數分析
- 5.2.2 init分析
- 5.2.3 systemReady分析
- 5.2.4 BootComplete處理
- 5.2.5 初識PowerManagerService總結
- 5.3 PMS WakeLock分析
- 5.3.1 WakeLock客戶端分析
- 5.3.2 PMS acquireWakeLock分析
- 5.3.3 Power類及LightService類介紹
- 5.3.4 WakeLock總結
- 5.4 userActivity及Power按鍵處理分析
- 5.4.1 userActivity分析
- 5.4.2 Power按鍵處理分析
- 5.5 BatteryService及BatteryStatsService分析
- 5.5.1 BatteryService分析
- 5.5.2 BatteryStatsService分析
- 5.5.3 BatteryService及BatteryStatsService總結
- 5.6 本章學習指導
- 5.7 本章小結
- 第6章 深入理解ActivityManagerService
- 6.1 概述
- 6.2 初識ActivityManagerService
- 6.2.1 ActivityManagerService的main函數分析
- 6.2.2 AMS的 setSystemProcess分析
- 6.2.3 AMS的 installSystemProviders函數分析
- 6.2.4 AMS的 systemReady分析
- 6.2.5 初識ActivityManagerService總結
- 6.3 startActivity分析
- 6.3.1 從am說起
- 6.3.2 AMS的startActivityAndWait函數分析
- 6.3.3 startActivityLocked分析
- 6.4 Broadcast和BroadcastReceiver分析
- 6.4.1 registerReceiver流程分析
- 6.4.2 sendBroadcast流程分析
- 6.4.3 BROADCAST_INTENT_MSG消息處理函數
- 6.4.4 應用進程處理廣播分析
- 6.4.5 廣播處理總結
- 6.5 startService之按圖索驥
- 6.5.1 Service知識介紹
- 6.5.2 startService流程圖
- 6.6 AMS中的進程管理
- 6.6.1 Linux進程管理介紹
- 6.6.2 關于Android中的進程管理的介紹
- 6.6.3 AMS進程管理函數分析
- 6.6.4 AMS進程管理總結
- 6.7 App的 Crash處理
- 6.7.1 應用進程的Crash處理
- 6.7.2 AMS的handleApplicationCrash分析
- 6.7.3 AppDeathRecipient binderDied分析
- 6.7.4 App的Crash處理總結
- 6.8 本章學習指導
- 6.9 本章小結
- 第7章 深入理解ContentProvider
- 7.1 概述
- 7.2 MediaProvider的啟動及創建
- 7.2.1 Context的getContentResolver函數分析
- 7.2.2 MediaStore.Image.Media的query函數分析
- 7.2.3 MediaProvider的啟動及創建總結
- 7.3 SQLite創建數據庫分析
- 7.3.1 SQLite及SQLiteDatabase家族
- 7.3.2 MediaProvider創建數據庫分析
- 7.3.3 SQLiteDatabase創建數據庫的分析總結
- 7.4 Cursor 的query函數的實現分析
- 7.4.1 提取query關鍵點
- 7.4.2 MediaProvider 的query分析
- 7.4.3 query關鍵點分析
- 7.4.4 Cursor query實現分析總結
- 7.5 Cursor close函數實現分析
- 7.5.1 客戶端close的分析
- 7.5.2 服務端close的分析
- 7.5.3 finalize函數分析
- 7.5.4 Cursor close函數總結
- 7.6 ContentResolver openAssetFileDescriptor函數分析
- 7.6.1 openAssetFileDescriptor之客戶端調用分析
- 7.6.2 ContentProvider的 openTypedAssetFile函數分析
- 7.6.3 跨進程傳遞文件描述符的探討
- 7.6.4 openAssetFileDescriptor函數分析總結
- 7.7 本章學習指導
- 7.8 本章小結
- 第8章 深入理解ContentService和AccountManagerService
- 8.1 概述
- 8.2 數據更新通知機制分析
- 8.2.1 初識ContentService
- 8.2.2 ContentResovler 的registerContentObserver分析
- 8.2.3 ContentResolver的 notifyChange分析
- 8.2.4 數據更新通知機制總結和深入探討
- 8.3 AccountManagerService分析
- 8.3.1 初識AccountManagerService
- 8.3.2 AccountManager addAccount分析
- 8.3.3 AccountManagerService的分析總結
- 8.4 數據同步管理SyncManager分析
- 8.4.1 初識SyncManager
- 8.4.2 ContentResolver 的requestSync分析
- 8.4.3 數據同步管理SyncManager分析總結
- 8.5 本章學習指導
- 8.6 本章小結