nativePollOnce()的實現函數是android\_os\_MessageQueue\_nativePollOnce,代碼如下:
**android_os_MessageQueue.cpp-->android_os_MessageQueue_nativePollOnce()**
```
static voidandroid_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jintptr, jint timeoutMillis)
NativeMessageQueue* nativeMessageQueue =
reinterpret_cast<NativeMessageQueue*>(ptr);
// 取出NativeMessageQueue對象,并調用它的pollOnce
nativeMessageQueue->pollOnce(timeoutMillis);
}
```
分析pollOnce函數:
**android_os_MessageQueue.cpp-->NativeMessageQueue::pollOnece()**
```
void NativeMessageQueue::pollOnce(inttimeoutMillis) {
mLooper->pollOnce(timeoutMillis); // 重任傳遞到Looper的pollOnce函數
}
```
Looper的pollOnce函數如下:
**Looper.cpp-->Looper::pollOnce()**
```
inline int pollOnce(int timeoutMillis) {
returnpollOnce(timeoutMillis, NULL, NULL, NULL);
}
```
上面的函數將調用另外一個有4個參數的pollOnce函數,這個函數的原型如下:
```
int pollOnce(int timeoutMillis, int* outFd, int*outEvents, void** outData)
```
其中:
- timeOutMillis參數為超時等待時間。如果為-1,則表示無限等待,直到有事件發生為止。如果值為0,則無需等待立即返回。
- outFd用來存儲發生事件的那個文件描述符 。
- outEvents用來存儲在該文件描述符1上發生了哪些事件,目前支持可讀、可寫、錯誤和中斷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_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句柄中 */
struct epoll_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_event resultEvents[10];
int timeout = -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發生了錯誤
} else if(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呢?
關于這個問題的答案,可參見鄧凡平的一篇博文“隨筆之如何實現一個線程池”。
- <http://www.cnblogs.com/innost/archive/2011/11/24/2261454.html>
#### 2. pollOnce()函數分析
下面分析帶4個參數的pollOnce()函數,代碼如下:
**Looper.cpp-->Looper::pollOnce()**
```
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-->Looper::pollInner()**
```
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
從這里也可看出Native Message和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函數返回,這時候可能有三種情況:
a) 發生錯誤,則跳轉到Done處。
b) 超時,這時候也跳轉到Done處。
c) epoll\_wait監測到某些文件句柄上有事件發生。
- 假設epoll\_wait因為文件句柄有事件而返回,此時需要根據文件句柄來分別處理:
a) 如果是管道讀這一端有事情,則認為是控制命令,可以直接讀取管道中的數據。
b) 如果是其他FD發生事件,則根據Request構造Response,并push到Response數組中。
- 真正開始處理事件是在有Done標志的位置。
a) 首先處理Native的Message。調用Native Handler的handleMessage處理該Message。
b) 處理Response數組中那些帶有callback的事件。
上面的處理流程還是比較清晰的,但還是有個一個攔路虎,那就是mRequests,下面就來清剿這個攔路虎。
#### 3. 添加監控請求
添加監控請求其實就是調用epoll\_ctl增加文件句柄。下面通過從Native的Activity找到的一個例子來分析mRequests。
**android_app_NativeActivity.cpp-->loadNativeCode_native()**
```
static jint
loadNativeCode_native(JNIEnv* env, jobject clazz,jstring path,
jstringfuncName,jobject messageQueue,
jstringinternalDataDir, jstring obbDir,
jstringexternalDataDir, int sdkVersion,
jobject jAssetMgr,jbyteArray savedState)
{
......
/* 調用Looper的addFd函數。第一個參數表示監聽的fd;第二個參數0表示ident;
第三個參數表示需要監聽的事件,這里為只監聽可讀事件;第四個參數為回調函數,當該fd發生
指定事件時,looper將回調該函數;第五個參數code為回調函數的參數 */
code->looper->addFd(code->mainWorkRead,0,
ALOOPER_EVENT_INPUT,mainWorkCallback, code);
......
}
```
Looper的addFd()代碼如下所示:
**Looper.cpp-->Looper::addFd()**
```
int Looper::addFd(int fd, int ident, int events,
ALooper_callbackFunccallback, void* data) {
if (!callback) {
/* 判斷該Looper是否支持不帶回調函數的文件句柄添加。一般不支持,因為沒有回調函數
Looper也不知道如何處理該文件句柄上發生的事情 */
if(! mAllowNonCallbacks) {
return -1;
}
......
}
#ifdef LOOPER_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-->Looper::pushResponse()**
```
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()了。sendMessage()的代碼如下:
**Looper.cpp-->Looper::sendMessage()**
```
void Looper::sendMessage(constsp<MessageHandler>& handler,
constMessage& message) {
//Native的sendMessage函數必須同時傳遞一個Handler
nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now, handler, message); //調用sendMessageAtTime
}
[Looper.java-->Looper::sendMessageAtTime()]
void Looper::sendMessageAtTime(nsecs_t uptime,
constsp<MessageHandler>& handler,
constMessage& message) {
size_t i= 0;
{
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();
}
}
```
- 前言
- 推薦序
- 第1章 開發環境部署
- 1.1獲取Android源代碼
- 1.2Android的編譯
- 1.3在IDE中導入Android源代碼
- 1.3.1將Android源代碼導入Eclipse
- 1.3.2將Android源代碼導入SourceInsight
- 1.4調試Android源代碼
- 1.4.1使用Eclipse調試Android Java源代碼
- 1.4.2使用gdb調試Android C/C 源代碼
- 1.5本章小結
- 第2章 深入理解Java Binder和MessageQueue
- 2.1概述
- 2.2Java層中的Binder分析
- 2.2.1Binder架構總覽
- 2.2.2初始化Java層Binder框架
- 2.2.3窺一斑,可見全豹乎
- 2.2.4理解AIDL
- 2.2.5Java層Binder架構總結
- 2.3心系兩界的MessageQueue
- 2.3.1MessageQueue的創建
- 2.3.2提取消息
- 2.3.3nativePollOnce函數分析
- 2.3.4MessageQueue總結
- 2.4本章小結
- 第3章 深入理解AudioService
- 3.1概述
- 3.2音量管理
- 3.2.1音量鍵的處理流程
- 3.2.2通用的音量設置函數setStreamVolume()
- 3.2.3靜音控制
- 3.2.4音量控制小結
- 3.3音頻外設的管理
- 3.3.1 WiredAccessoryObserver 設備狀態的監控
- 3.3.2AudioService的外設狀態管理
- 3.3.3音頻外設管理小結
- 3.4AudioFocus機制的實現
- 3.4.1AudioFocus簡單的例子
- 3.4.2AudioFocus實現原理簡介
- 3.4.3申請AudioFocus
- 3.4.4釋放AudioFocus
- 3.4.5AudioFocus小結
- 3.5AudioService的其他功能
- 3.6本章小結
- 第4章 深入理解WindowManager-Service
- 4.1初識WindowManagerService
- 4.1.1一個從命令行啟動的動畫窗口
- 4.1.2WMS的構成
- 4.1.3初識WMS的小結
- 4.2WMS的窗口管理結構
- 4.2.1理解WindowToken
- 4.2.2理解WindowState
- 4.2.3理解DisplayContent
- 4.3理解窗口的顯示次序
- 4.3.1主序、子序和窗口類型
- 4.3.2通過主序與子序確定窗口的次序
- 4.3.3更新顯示次序到Surface
- 4.3.4關于顯示次序的小結
- 4.4窗口的布局
- 4.4.1從relayoutWindow()開始
- 4.4.2布局操作的外圍代碼分析
- 4.4.3初探performLayoutAndPlaceSurfacesLockedInner()
- 4.4.4布局的前期處理
- 4.4.5布局DisplayContent
- 4.4.6布局的階段
- 4.5WMS的動畫系統
- 4.5.1Android動畫原理簡介
- 4.5.2WMS的動畫系統框架
- 4.5.3WindowAnimator分析
- 4.5.4深入理解窗口動畫
- 4.5.5交替運行的布局系統與動畫系統
- 4.5.6動畫系統總結
- 4.6本章小結
- 第5章 深入理解Android輸入系統
- 5.1初識Android輸入系統
- 5.1.1getevent與sendevent工具
- 5.1.2Android輸入系統簡介
- 5.1.3IMS的構成
- 5.2原始事件的讀取與加工
- 5.2.1基礎知識:INotify與Epoll
- 5.2.2 InputReader的總體流程
- 5.2.3 深入理解EventHub
- 5.2.4 深入理解InputReader
- 5.2.5原始事件的讀取與加工總結
- 5.3輸入事件的派發
- 5.3.1通用事件派發流程
- 5.3.2按鍵事件的派發
- 5.3.3DispatcherPolicy與InputFilter
- 5.3.4輸入事件的派發總結
- 5.4輸入事件的發送、接收與反饋
- 5.4.1深入理解InputChannel
- 5.4.2連接InputDispatcher和窗口
- 5.4.3事件的發送
- 5.4.4事件的接收
- 5.4.5事件的反饋與發送循環
- 5.4.6輸入事件的發送、接收與反饋總結
- 5.5關于輸入系統的其他重要話題
- 5.5.1輸入事件ANR的產生
- 5.5.2 焦點窗口的確定
- 5.5.3以軟件方式模擬用戶操作
- 5.6本章小結
- 第6章 深入理解控件系統
- 6.1 初識Android的控件系統
- 6.1.1 另一種創建窗口的方法
- 6.1.2 控件系統的組成
- 6.2 深入理解WindowManager
- 6.2.1 WindowManager的創建與體系結構
- 6.2.2 通過WindowManagerGlobal添加窗口
- 6.2.3 更新窗口的布局
- 6.2.4 刪除窗口
- 6.2.5 WindowManager的總結
- 6.3 深入理解ViewRootImpl
- 6.3.1 ViewRootImpl的創建及其重要的成員
- 6.3.2 控件系統的心跳:performTraversals()
- 6.3.3 ViewRootImpl總結
- 6.4 深入理解控件樹的繪制
- 6.4.1 理解Canvas
- 6.4.2 View.invalidate()與臟區域
- 6.4.3 開始繪制
- 6.4.4 軟件繪制的原理
- 6.4.5 硬件加速繪制的原理
- 6.4.6 使用繪圖緩存
- 6.4.7 控件動畫
- 6.4.8 繪制控件樹的總結
- 6.5 深入理解輸入事件的派發
- 6.5.1 觸摸模式
- 6.5.2 控件焦點
- 6.5.3 輸入事件派發的綜述
- 6.5.4 按鍵事件的派發
- 6.5.5 觸摸事件的派發
- 6.5.6 輸入事件派發的總結
- 6.6 Activity與控件系統
- 6.6.1 理解PhoneWindow
- 6.6.2 Activity窗口的創建與顯示
- 6.7 本章小結
- 第7章 深入理解SystemUI
- 7.1 初識SystemUI
- 7.1.1 SystemUIService的啟動
- 7.1.2 狀態欄與導航欄的創建
- 7.1.3 理解IStatusBarService
- 7.1.4 SystemUI的體系結構
- 7.2 深入理解狀態欄
- 7.2.1 狀態欄窗口的創建與控件樹結構
- 7.2.2 通知信息的管理與顯示
- 7.2.3 系統狀態圖標區的管理與顯示
- 7.2.4 狀態欄總結
- 7.3 深入理解導航欄
- 7.3.1 導航欄的創建
- 7.3.2 虛擬按鍵的工作原理
- 7.3.3 SearchPanel
- 7.3.4 關于導航欄的其他話題
- 7.3.5 導航欄總結
- 7.4 禁用狀態欄與導航欄的功能
- 7.4.1 如何禁用狀態欄與導航欄的功能
- 7.4.2 StatusBarManagerService對禁用標記的維護
- 7.4.3 狀態欄與導航欄對禁用標記的響應
- 7.5 理解SystemUIVisibility
- 7.5.1 SystemUIVisibility在系統中的漫游過程
- 7.5.2 SystemUIVisibility發揮作用
- 7.5.3 SystemUIVisibility總結
- 7.6 本章小結
- 第8章 深入理解Android壁紙
- 8.1 初識Android壁紙
- 8.2深入理解動態壁紙
- 8.2.1啟動動態壁紙的方法
- 8.2.2壁紙服務的啟動原理
- 8.2.3 理解UpdateSurface()方法
- 8.2.4 壁紙的銷毀
- 8.2.5 理解Engine的回調
- 8.3 深入理解靜態壁紙-ImageWallpaper
- 8.3.1 獲取用作靜態壁紙的位圖
- 8.3.2 靜態壁紙位圖的設置
- 8.3.3 連接靜態壁紙的設置與獲取-WallpaperObserver
- 8.4 WMS對壁紙窗口的特殊處理
- 8.4.1 壁紙窗口Z序的確定
- 8.4.2 壁紙窗口的可見性
- 8.4.3 壁紙窗口的動畫
- 8.4.4 壁紙窗口總結
- 8.5 本章小結