Handler,咱們Android工程師天天都在用的東西,它主要能夠用來作線程間通訊,這一塊知識其實咱們很熟悉了,可是最近我又翻了一遍Handler源碼,發現我對Handler又有了新的認識,因此總結了這篇文章記錄一下, 本文的源代碼基於8.0。java
在文章開始以前,友情提示,我會默認你們有了解Handler,Looper,MessageQueue的基礎知識,不清楚的同窗能夠自行查閱別的資料哈。android
因爲Handler在Framework層的代碼量也是比較大的,一篇文章不可能面面俱到,因此我打算從Handler的使用入手,只對關鍵節點進行深刻,這樣既不會太陷入細節,也能把握Handler背後的整個流程。面試
先看一下Handler的使用,咱們先定義一個MyHandler靜態內部類以下:編程
private static class MyHandler extends Handler {
WeakReference<MainActivity> mMainActWeakRef;
public MyHandler(MainActivity activity) {
this.mMainActWeakRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
複製代碼
而後咱們new一個MyHandler對象,就能夠經過向MyHandler發消息來實現咱們的邏輯了。ubuntu
// MainActivity,onCreate
mMyHandler = new MyHandler(this);
mMyHandler.sendMessage(new Message());
複製代碼
咱們調用了sendMessage方法,最終會觸發mMyHandler的handleMessage方法被回調,參數就是咱們發送的Message。咱們看下sendMessage的實現bash
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
能夠看到,先調用Handler的sendMessageDelayed方法,因爲delay爲0,因此消息的發送時間是SystemClock.uptimeMillis()得到的當前時間,接着調用enqueueMessage方法,內部調用了MessageQueue的enqueueMessage方法,具體發送到哪一個MessageQueue由Handler初始化時綁定的Looper決定的,通常咱們在主線程new的Handler默認使用MainLooper,MainLooper的初始化在ActivityThread的main方法中。數據結構
接下來咱們看下MessageQueue的enqueueMessage方法的實現,關鍵方法多已經添加註釋了。less
boolean enqueueMessage(Message msg, long when) {
// ....
synchronized (this) {
// 設置消息inUse標識
msg.markInUse();
// 消息什麼時候被處理
msg.when = when;
// mMessages是消息隊列頭,注意數據結構是單鏈表
Message p = mMessages;
boolean needWake;
// 若是隊列是空,或者when的時間爲0,或者當前插入的時間小於表頭消息時間
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// 將消息插入表頭,並檢查當前是否被阻塞,若是被阻塞,設置needWake標識
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; // 這一坨邏輯就是將新的msg插入到一個合適的位置,按照when字段從小到大排序 for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. // 若是須要喚醒,喚醒Looper if (needWake) { nativeWake(mPtr); } } return true; } 複製代碼
因此插入消息到消息隊列是當即插入的,按照消息的when字段來排序,若是是delay的消息,它的when就是當前時間+delay時間,最後有一個很關鍵的方法調用 nativeWake(mPtr),它的做用是喚醒Looper,具體實現這裏先放一放,後面會作詳細分析。異步
先來複習一下Looper的知識,咱們知道Handler須要綁定一個Looper,主線程建立的Handler默認綁定的就是主線程的Looper,固然在子線程也能夠用Looperasync
// 子線程中使用Looper
Looper.prepare();
Looper.loop();
複製代碼
prepare方法就是新建一個Looper,並設置給sThreadLocal對象。 咱們重點看loop方法的實現
public static void loop() {
// ...省略檢查代碼...
for (;;) {
// 從MessageQueue中獲取下一條消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ...省略代碼...
try {
// 消息分發
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ...省略代碼...
}
}
複製代碼
咱們看到先是一個死循環,而後調用了queue.next()這個方法,從MessageQueue獲取下一條消息,並且可能會阻塞,咱們繼續跟進next方法的實現
// MessageQueue.java
Message next() {
// 獲取ptr,這是NativeMessageQueue的指針
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
// pollOnce,會阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
// 從消息隊列中拿消息,注意這裏加鎖了
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// ...省略了一坨代碼...
// 執行idle Handler
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
}
}
複製代碼
方法比較長,不重要的代碼我已經省略了,咱們先列下重點流程,而後逐個分析。
mPtr的初始化是在MessageQueue的構造方法中初始化的
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
複製代碼
nativeInit是一個Jni方法,繼續跟進
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
複製代碼
能夠看到new了一個NativeMessageQueue對象(它繼承與RefBase,是一個智能指針對象),而後將它的引用計數+1,這樣就不會自動回收,最後將它的對象指針地址返回。因此咱們就清楚了,應用層MessageQueue中的mPtr就是Native層NativeMessageQueue的指針。
nativePollOnce也是一個Jni方法,咱們看下實現
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
複製代碼
先將參數的mPtr轉化爲NativeMessageQueue指針,而後調用了pollOnce方法,繼續跟進
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
複製代碼
調用了mLooper的pollOnce,這個mLooper是native層的Looper對象,咱們繼續跟進
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
// 省略了一坨代碼
result = pollInner(timeoutMillis);
}
}
複製代碼
調用了自身的pollInnner方法
int Looper::pollInner(int timeoutMillis) {
// 省略了一坨代碼
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// 省略了一坨代碼
}
複製代碼
這裏只貼出關鍵代碼,咱們看到調用了epoll_wait方法,這個方法可能會阻塞,正由於這個方法,因此咱們前面介紹的Looper中的死循環不會致使主線程一直在空轉(面試常見考點)。這個方法返回會有幾種狀況。
當nativePollOnce方法返回後,進入下一步
咱們從新回到MessageQueue.java中,這裏再貼一次代碼,我直接在代碼中添加註釋了
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
// 獲取消息隊列的頭
Message msg = mMessages;
// 若是msg.target==null,說明這是一個消息屏障,若是是消息屏障,會遍歷整個消息隊列,
// 去搜索是否有異步消息,若是有異步消息,會將第一個異步消息取出
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 若是消息還沒到時間,Looper繼續休眠
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
// 從消息隊列移除消息,並返回
return msg;
}
} else {
// No more messages.
// 若是消息隊列是空,繼續休眠,並且休眠時間是永久,直到被喚醒
nextPollTimeoutMillis = -1;
}
複製代碼
這裏涉及到二個概念,須要解釋一下
Message message = Message.obtain();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
message.setAsynchronous(true);
}
mMyHandler.sendMessageAtFrontOfQueue(message);
複製代碼
當消息隊列中沒有消息或者消息隊列如今被消息屏障阻塞着的時候,會執行idle handlers,因此它的意義在於,當消息隊列中沒有任務在執行時,給咱們一個回調,讓咱們有機會去執行一些非緊急任務,而不用擔憂搶佔主線程緊急任務的CPU執行時間片,由於此刻消息隊列是空閒狀態,Idle Handler的一個典型應用就是:咱們常常在App啓動的時候會用到Idle Handler來執行一些非緊急的初始化任務。能夠經過下面的代碼向MessageQueue中添加IdleHandler
mMessageQueue.addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
return true;
}
});
複製代碼
這裏queueIdle的返回值是有意義的,若是返回false,IdleHandler被回調後就會被移除,也就是說只會回調一次,若是返回true,會屢次調用。固然IdleHandler也是執行在主線程,若是作太重的任務,仍然有可能會阻塞消息隊列,致使後面的消息處理不及時。
前面已經說過了,Looper調用MessageQueue的next方法會阻塞,它的底層實現是epoll_wait,那麼在阻塞的時候,若是有新消息過來,確定是要喚醒Looper來處理消息。喚醒方法咱們 前面提到過,咱們再回到MessageQueue的enquque方法
if (needWake) {
nativeWake(mPtr);
}
複製代碼
它也是一個native方法,調用是NativeMessageQueue的wake方法,它最終調用的是native層Looper的wake方法。
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
void Looper::wake() {
// 省略判斷代碼
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
// 省略判斷代碼
}
複製代碼
在Looper的wake方法中,調用了write方法,它的第一個參數是mWakeEventFd,其實就是往mWakeEventFd這個描述符對應的文件中寫入了一個1,很顯然就是這個寫入將把Looper從epoll_wait的等待 中喚醒,那mWakeEventFd跟咱們的epoll_wait有什麼關係呢?
咱們查看下Looper的構造函數
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
strerror(errno));
AutoMutex _l(mLock);
rebuildEpollLocked();
}
複製代碼
能夠看到在構造器內,mWakeEventFd被初始化,而後調用了rebuildEpollLocked()方法,看名字應該跟構建Epoll有關,跟進去看看
void Looper::rebuildEpollLocked() {
// 省略一坨代碼
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
// 省略一坨代碼
}
複製代碼
先建立了mEpollFd,接着調用epoll_ctl方法,這個方法就是將mEpollFd和mWakeEventFd關聯起來,當向mWakeEventFd中寫入數據時,mEpollFd將會被喚醒,epoll_wait方法就會返回。到這裏其實就很清楚了,咱們上面的wake方法,就是向mWakeEventFd中寫入了一個1,來喚醒mEpollFd。
消息的分發在Lopper.java類中,咱們看下實現
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
複製代碼
最終調用了msg的target字段來分發,咱們前面說過這個target就是Handler對象
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
這段邏輯很簡單,這裏簡單提一下,先判斷msg是否有callback,若是沒有再判斷Handler是否有Callback,若是沒有,才最終回調Handler的handleMessage方法來處理。
到這裏,Android中Handler的整個原理就講完了,咱們從Message的整個生命週期入手,從消息被建立,到加入消息隊列,到被消費處理而後被回收,咱們都深刻到了native層,瞭解了底層的技術原理。
本文主要分析瞭如下幾點
瞭解這些原理,能夠指導咱們寫出更優的應用代碼,但願本文對你有幫助!
爲何會有這個想法呢?由於我相信大部分同窗包括我本身,對Linux都算不上熟悉,看源碼內的不少系統調用很容易一臉懵逼,這樣理解源碼起來就很困難。因此我就一直想能有個機會真實操做一把Linux編程,恰好此次分析的Handler底層利用的epoll不是很難,我就擼了一個demo來感覺一下Linux編程,直接上代碼(環境是ubuntu 14, 編譯器是clion)。
需求很是簡單:主進程fork一個子進程出來,而後主進程epoll_wait等待,子進程先sleep 2s,而後喚醒主進程,程序結束。
#include <sys/eventfd.h>
#include <sys/epoll.h>
static const int EPOLL_SIZE_HINT = 8;
int main(void) {
// 建立時間描述符
int mEventFd = eventfd(0, 0);
if (mEventFd == -1) {
printf("create efd error!");
exit(0);
}
// 建立epoll描述符
int mEpollFd = epoll_create(EPOLL_SIZE_HINT);
struct epoll_event eventItem;
eventItem.events = EPOLLIN;
eventItem.data.fd = mEventFd;
// 綁定事件描述符到epoll
epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mEventFd, &eventItem);
// 建立一個子進程,注意fork出來的進程繼承主進程的內存佈局,而vfork不會
_pid_t pid = fork();
// 注意fork方法會返回二次,==0 是在子進程,>0在父進程
if (pid == 0) {
// 子進程
printf("child process sleep 2s\n");
// 先sleep 2s
sleep(2);
uint64_t event = 1;
// 寫入1到mEventFd, 喚醒父進程的epoll
write(mEventFd, &event, sizeof(uint64_t);
printf(child process write event finish!\n);
} else {
// 父進程
struct epoll_event eventItems[EPOLL_SIZE_HINT * 100];
printf("parent process start wating!\n");
// 父進程epoll阻塞等待,最後一個參數是-1,表示永遠等待
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_SIZE_HINT*100, -1);
// 被喚醒 epoll_wait返回,
printf("get event : %d\n", eventCount);
}
exit(0);
}
複製代碼
控制檯輸出結果以下:
代碼不難,Linux環境能夠直接跑的,你們能夠本身動手敲一遍,加深理解! 以上!!