Handler機制源碼分析

目錄

image

前言

Handler使用,請參見:Handler總結(jié)和補充_fdsafwagdagadg6576的專欄-CSDN博客

正文

Hander源碼機制包括四部分源碼:
Handler源碼,Looper源碼Message源碼,Messagequeue源碼.
分成java層和native層.
消息流程
handler---messageQueue---Looper---hander,消息發(fā)送---消息接收---消息分發(fā)---消息處理.
類圖關(guān)系

handler_java

1 Handler類

1.1 Handler 構(gòu)造函數(shù)初始化

  1. 無參數(shù)構(gòu)造函數(shù)
public class Handler {
    /**我們通常用于創(chuàng)建Handler的構(gòu)造方法之一*/
    public Handler() {
        this(null, false);
    }
    //===========step1: 構(gòu)造函數(shù)====================
    public Handler(Callback callback, boolean async) {
        ......
        //=======step2://重點;獲取Looper(messagequeue管理者)=== /
        mLooper = Looper.myLooper();
        //=======step3: 獲取Looper的Messagequeue=====
        mQueue = mLooper.mQueue;  
        //回調(diào)函數(shù)默認(rèn)是Null  
        mCallback = callback; 
        //設(shè)置消息是否為異步處理方式 
        mAsynchronous = async; 
    }

根據(jù)調(diào)用關(guān)系:
step1: Handler構(gòu)造函數(shù)中會去創(chuàng)建一個Looper對象。handler和Looper綁定,同時綁定Looper的messageQueue。
step2: Looper.myLooper()獲取Looper.
step3: mLooper.mQueue 獲取Looper的MessageQueue.

  1. 有參構(gòu)造函數(shù)
public Handler(Looper looper) {
    this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Handler類在構(gòu)造方法中,可指定Looper,Callback回調(diào)方法以及消息的處理方式(同步或異步),對于無參的handler,默認(rèn)是當(dāng)前線程的Looper.
mainHandler = new Handler() 等價于 new Handler(Looper.myLooper())
Looper.myLooper():獲取當(dāng)前進(jìn)程的looper對象。詳見下文.
1.2 Handler send message

call laddder:
sendEmptyMessage
--sendEmptyMessageDelayed
----sendMessageDelayed
------sendMessageAtTime
--------enqueueMessage
/////////////////////////////////////////////////////////////////////////
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    ......
    return enqueueMessage(queue, msg, uptimeMillis);
}
// 最后調(diào)用此方法添加到消息隊列中
private boolean enqueueMessage(MessageQueue queue, Message msg,long uptimeMillis) {
    msg.target = this;// 設(shè)置發(fā)送目標(biāo)對象是Handler本身
    ......
    return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息隊列中
}

step1: Message.obtain
step2:enqueueMessage(queue, msg, uptimeMillis)
step3: msg.target = this;// 設(shè)置發(fā)送目標(biāo)對象是Handler本身
return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息隊列中
1.3 消息處理
case1: handleMessage(msg);
是派生一個Hanlder子類并重寫其handleMessage方法來處理具體的消息。
發(fā)送發(fā)是sendMessage.
case 2:handleCallback & mCallback.handleMessage(msg)
用callback來創(chuàng)建一個Handler的實例而無需派生Handler的子類。而Callback給我們提供了另外一種方式,那就是當(dāng)我們不想派生子類的時候,可以通過Callback來實現(xiàn).

call ladder: 
post
--sendMessageDelayed
----getPostMessage
------m.callback = r//Runnable對象
--------new Handler(callback)

Message的callback是什么?其實就是一個Runnable對象,實際上就是Handler的post方法所傳遞的Runnable參數(shù)

private static void handleCallback(Message message) {
        message.callback.run();
}

發(fā)送方:

public final boolean post(Runnable r)
{
       return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
}

最后要注意的是寫在Looper.loop()之后的代碼不會被執(zhí)行,這個函數(shù)內(nèi)部應(yīng)該是一個循環(huán),當(dāng)調(diào)用mHandler.getLooper().quit()后,loop()才會中止,其后的代碼才能得以運行.
1.4 異常處理
內(nèi)存泄漏

//檢查Handler是否是static的;如果不是的,那么有可能導(dǎo)致內(nèi)存泄露
        if (FIND_POTENTIAL_LEAKS) {  
            final Class<? extends Handler> klass = getClass();  
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {  
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());  
            }  
        }

小結(jié):handler類:1) init 綁定looper,queue; 2) send msg 3) handle msg .
handler模型是以消息為中心,將消息放入主線程,消息綁定了handler對象,進(jìn)而調(diào)用了handler執(zhí)行函數(shù)。實現(xiàn)了對象,消息和函數(shù)綁定.
handler.sendmessage(msg)--msg.target.handlemsg(msg).
改寫成以對象為中心的實現(xiàn)call ladder:Looper.enqueuemessage(handler)---handler.handlemessage(handler.msg).

2 Looper類

looper類:1) create queue 2) 循環(huán)讀取 3) 分發(fā)
looper是兩個線程,一個線程send message放入msg,一個線程epoll_wait 獲取消息.
Looper.loop()循環(huán)取Message中的消息,回調(diào)msg.target.dispatchMessage(msg)。
Handler sendMessage 調(diào)用enqueueMessage,enqueueMessage第一行msg.target = this;這個this是什么呢?這個this在handler方法中自然是handler本身了,所以msg.target.dispatchMessage(msg);可以找到正確的handler,消息分開不會出錯.
2.1 創(chuàng)建MessageQueue

//Looper類的構(gòu)造方法創(chuàng)建了消息隊列MessageQueue對象
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mRun = true;
    mThread = Thread.currentThread();
}

一個線程一個Looper一個MessageQueue.
2.2 獲取Looper對象
通過ThreadLocal獲取Looper對象

//handler調(diào)用的獲取Looper對象的方法。實際是在ThreadLocal中獲取。
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

2.3 Looper 主循環(huán)
Looper 讀取message & 分發(fā)message

//looper中最重要的方法loop(),該方法是個死循環(huán),會不斷去消息隊列MessageQueue中獲取消息,
//然后調(diào)dispatchMessage(msg)方法去執(zhí)行
public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
        //進(jìn)入loop的主循環(huán)方法
        for (;;) {
            Message msg = queue.next(); // might block
            ......
            //msg.target is handler;分發(fā)消息
            msg.target.dispatchMessage(msg);
            msg.recycle();
         }
}
  • Message msg= queue.next() ; 讀取MessageQueue的下一條Message;
  • msg.target.dispatchMessage(msg);把Message分發(fā)給相應(yīng)的target;
    next源碼分析,參見MessageQueue類分析
    下面是dispatchMessage分析
// 在looper類中的loop()方法內(nèi)部調(diào)用的方法
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }

分發(fā)給handleMessage 和handleCallback 處理.

2.4 子線程創(chuàng)建Looper(擴(kuò)展)

如果子線程中使用Looper.prepare()和Looper.loop()創(chuàng)建了消息隊列,就可以讓消息處理在該線程中完成.
Looper.prepare() 通過map ThreadLocal綁定線程和新建的Looper(包含MessageQueue).判斷l(xiāng)ooper在哪個線程內(nèi)運行。
Looper.loop()不斷地從當(dāng)前線程對應(yīng)Looper的MessageQueue中取出消息進(jìn)行處理.

//perpare()方法,用來初始化一個Looper對象
public static void prepare() {
    prepare(true);
}       
private static void prepare(boolean quitAllowed) {
    ......
    sThreadLocal.set(new Looper(quitAllowed));
}

在非主線程中直接new Handler() 會報如下的錯誤: Can't create handler inside thread that has not called Looper.prepare() 原因是非主線程中默認(rèn)沒有創(chuàng)建Looper對象,需要先調(diào)用Looper.prepare()啟用Looper,然后再調(diào)用Looper.loop()。
子線程自己創(chuàng)建Looper

class childThread extends Thread{
        public Handler mHandler;
        @Override
        public void run() {
            //子線程中必須先創(chuàng)建Looper
            Looper.prepare();

            mHandler =new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //處理消息
                }
            };
            //啟動looper循環(huán)
            Looper.loop();
        }
    }

2.5 ThreadLocal
ThreadLocal機制:通常的全局變量,各個子線程是共享的。ThreadLocal變量特殊性是,它是global變量,但是每個子線程的ThreadLocal值是私有的.
原理是內(nèi)部有個map管理每個線程和對應(yīng)的變量.感興趣的同學(xué)可以在網(wǎng)上搜顯示源碼.
ThreadLocal和線程局部變量又什么區(qū)別?線程局部變量完全可以替代ThreadLocal,但是ThreadLocal使用簡單,java采用局部變量的方法比較少.兩者的關(guān)系錢存在自己家和銀行,實際是一樣. Threadlocal 可以使一個Looper類統(tǒng)一管理所有線程的looper.
ThreadLocal與局部變量 ThreadLocal與局部變量_Jack Cheung-CSDN博客

3 MessageQueue類

線程同步在messageQueue的native層實現(xiàn).
消息的循環(huán),發(fā)送和處理
1) 生產(chǎn)者線程和消費者線程同步:
如果messageQueue為空: messageQueued.next 通過epoll監(jiān)聽messagequeue綁定的文件fd,實現(xiàn)讀寫同步.
如果messageQueue不為空: 生產(chǎn)者線程寫,消費者線程讀
nativePollOnce 阻塞,nativeWake 喚醒

//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
    //...
    synchronized (this) {
        boolean needWake;
        Message p = mMessages;
         //如果處于阻塞狀態(tài),并且鏈表頭部是一個同步屏障,并且插入消息是最早的異步消息,需要喚醒
            needWake = mBlocked && p.target == null && msg.isAsynchronous();            
            Message prev;
         //下面是一個鏈表的插入操作,將消息按時間順序插入到mMessages中
            for (;;) {
                prev = p;
                p = p.next;
               //如果在找到插入位置之前,發(fā)現(xiàn)了異步消息的存在,不需要喚醒
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }        
        //nativeWake方法會喚醒當(dāng)前線程的MessageQueue
        if (needWake) {
            nativeWake(mPtr);
        }     
}

next() :提取下一條message.在這里messagequeue 阻塞. epoll 監(jiān)聽與messagequeue綁定的文件fd,詳見:玩Android必看:從源碼角度看Handler - 嗶哩嗶哩

Message next() {
    final long ptr = mPtr;
    ......
    for (;;) {
        //阻塞操作,當(dāng)?shù)却齨extPollTimeoutMillis時長,或者消息隊列被喚醒,都會返回
        nativePollOnce(ptr, nextPollTimeoutMillis);
    ......
    }
}
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jint ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(timeoutMillis);
}
void NativeMessageQueue::pollOnce(int timeoutMillis) {
    mLooper->pollOnce(timeoutMillis);
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
        result = pollInner(timeoutMillis);
}
int Looper::pollInner(int timeoutMillis) {
    ......
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeReadPipeFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            }
    }
}
void Looper::awoken() {
    ......
    char buffer[16];
    ssize_t nRead;
    do {
        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}

2) 多個生產(chǎn)者線程同時寫:使用messageQueued.enqueueMessage中的鎖同步.
messageQueue 從java-jni-native c++流程,參見:Android消息機制2-Handler(Native層) - Gityuan博客 | 袁輝輝的技術(shù)博客

4 Message.java源碼:

讀寫Parcel數(shù)據(jù)

    private void readFromParcel(Parcel source) {
        what = source.readInt();
        arg1 = source.readInt();
        arg2 = source.readInt();
        if (source.readInt() != 0) {
            obj = source.readParcelable(getClass().getClassLoader());
        }
        when = source.readLong();
        data = source.readBundle();
        replyTo = Messenger.readMessengerOrNullFromParcel(source);
        sendingUid = source.readInt();
    }
public void writeToParcel(Parcel dest, int flags) 
{ 
     Parcelable p = (Parcelable)obj;   
     dest.writeInt(1); 
     dest.writeParcelable(p, flags); 
     ......
}

QA:

  1. 主線程中的looper是一直輪詢嗎?
    不是。如果MessageQueue沒有消息時,就會阻塞在loop的queue.next()方法里. 這時候主線程會釋放CPU資源進(jìn)入休眠狀態(tài),直到有下個消息進(jìn)來時候就會喚醒主線程. 使用Linux pipe/epoll機制,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作。原理類似于I/O,讀寫是堵塞的,不占用CPU資源。 \
  2. handler中發(fā)送消息線程安全嗎?
    線程安全。因為使用lock進(jìn)行了線程同步.
    MessageQueue中插入消息enqueueMessage方法以及取消息next都是線程安全的,都使用了synchronized進(jìn)行加鎖.
    小結(jié):
    Handler源碼:主要是綁定looper,sendmessage,handlemessage.
    Looper源碼: 主要是prepare-->Loop { queue.next;msg.target.dispatchMessage(msg); -->sThreadLocal.set(new Looper)}
Messagequeue.java源碼:
Message next() {
    nativePollOnce//epoll wait
}
enqueueMessage {
   nativeWake //epoll wake 
}
Message.java源碼
readFromParcel,writeToParcel

附錄源碼:Handler.java, Looper.java , Message.java,MessageQueue.java

類似文章: Android的消息機制(java層) Android的消息機制(java層) - 簡書
Android消息機制(native層) Android消息機制(native層) - 簡書

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容