日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Handler源码剖析

發布時間:2023/12/31 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Handler源码剖析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是handler?

熟悉Android開發的一定都知道Handler對于Android開發的重要性吧,Android主線程(UI線程)阻塞5s以上就會ANR,所以通常情況下耗時操作都是在子線程完成,當子線程完成耗時操作后,在通過Handler通知主線程去更新UI,最常見的使用場景就是在網絡請求完成的時候,將網絡請求的數據傳給主線程,所以Handler的作用就是完成線程間通信。

最簡單的線程間通信

我們想一下Android中Handler的使用步驟吧。

  • 主線程中定義一個Handler,并覆寫它的handlerMessage方法。
private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);//update UI} }; 復制代碼
  • 在子線程耗時操作完成后創建一個message對象,并通過調用handler.sendMessage方法將這個message發送給主線程
new Thread(new Runnable() {@Overridepublic void run() {// do sometingMessage message = Message.obtain();mHandler.sendMessage(message);} }).start(); 復制代碼

Java程序中要實現線程間通信要如何做呢?

  • 先定義一個Message對象,這里我們只在Message中定義一個字符串
public class Message {public String msg; } 復制代碼
  • 再定義一個IHandler接口
public interface IHandler {void handleMessage(Message msg); } 復制代碼
  • 下面我們寫一段普通的java代碼
public class Main {public static void main(String[] args) {IHandler handler = new IHandler() {@Overridepublic void handleMessage(Message message) {System.out.println("main thread receive a message: " + message.msg);}};new SubThread(handler).start();}private static class SubThread extends Thread {private IHandler mHandler;public SubThread(IHandler handler) {mHandler = handler;}@Overridepublic void run() {super.run();while (true) {try {Thread.sleep(1000);Message message = new Message();message.msg = "hello";mHandler.handleMessage(message);} catch (InterruptedException e) {e.printStackTrace();}}}} } 復制代碼

運行結果:

main thread receive a message: hello main thread receive a message: hello main thread receive a message: hello ······ ······ 復制代碼

這就是最簡單的線程通信,通過接口回調將子線程中的Message傳遞給主線程去處理。Android中的Handler當然不是用這種方式實現的了,但是對于類與類之間的通信,接口回調是最簡單最通用的方法了

下面我們參照Android中的方式實現一個Handler工具吧。Android的Handler在Java層包括了Handler,Looper,Message和MessageQueue四個類。其中Handler的主要作用是發送和處理消息,sendMessage會將新的Message加入到MessageQueue中。Looper是一個輪詢器,檢查MessageQueue中是否有Message,如果有Message就取出來分發給對應的Handler去處理。

  • Message不用多做處理,只需要增加一個目標Handler就可以了
public class Message {public Handler target;public String msg; } 復制代碼
  • MessageQueue是一個先進先出的隊列,為了方便實現我們使用java提供的LinkedBlockingQueue來實現
public class MessageQueue {private LinkedBlockingQueue<Message> messageList;public MessageQueue() {messageList = new LinkedBlockingQueue<>();}public void enqueueMessage(Message msg) {messageList.add(msg);}public Message next() {return messageList.poll();} } 復制代碼
  • Looper是和線程綁定的,一個線程里只能有一個Looper,所以Looper應該保存在ThreadLocal中。Looper的初始化應該放到Looper.prepare中去完成。
public class Looper {private static ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();final MessageQueue mQueue;final Thread mThread;public Looper() {mQueue = new MessageQueue();mThread = Thread.currentThread();}public static void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper());}public static Looper myLooper() {return sThreadLocal.get();}public static void loop() {final Looper me = myLooper();final MessageQueue queue = me.mQueue;for (; ; ) {Message msg = queue.next();if (msg == null) {continue;}msg.target.dispatchMessage(msg);}} } 復制代碼
  • 最后看一下Handler的實現
public class Handler {private Looper mLooper;private MessageQueue mQueue;public Handler() {mLooper = Looper.myLooper();mQueue = mLooper.mQueue;}public void sendMessage(Message msg) {msg.target = this;mQueue.enqueueMessage(msg);}public void dispatchMessage(Message msg) {handleMessage(msg);}public void handleMessage(Message msg) {} } 復制代碼

我們依然用之前例子里的代碼來測試

public class Main {public static void main(String[] args) {Looper.prepare();Handler handler = new Handler() {@Overridepublic void handleMessage(Message message) {super.handleMessage(message);System.out.println("main thread receive a message: " + message.msg);}};new SubThread(handler).start();Looper.loop();}private static class SubThread extends Thread {private Handler mHandler;public SubThread(Handler handler) {mHandler = handler;}@Overridepublic void run() {super.run();while (true) {try {Thread.sleep(1000);Message message = new Message();message.msg = "hello";mHandler.handleMessage(message);} catch (InterruptedException e) {e.printStackTrace();}}}} } 復制代碼

輸出結果

main thread receive a message: hello main thread receive a message: hello main thread receive a message: hello ······ ······ 復制代碼

一個簡單的Handler模擬程序就這樣完成了。

深入分析

上面我們用幾十行代碼實現的那個玩具Handler只是為了讓我們更好的理解Handler機制,Android實際中的Handler要復雜的多,更重要的是Android中Handler的實現不止包含了java代碼,還包含了native調用

private native static long nativeInit(); private native static void nativeDestroy(long ptr); private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ private native static void nativeWake(long ptr); private native static boolean nativeIsPolling(long ptr); private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); 復制代碼

這幾個native方法主要的作用是什么呢?在之前的Demo程序我們的loop方法實現是如果Message為空的時候就continue,這就造成了明明消息隊列里沒有消息,Looper依然在那里空轉浪費cpu資源。native方法就是解決這個問題的。

MessageQueue的創建

這幾個native方法都是在MessageQueue中定義,所以我們就先來研究一下MessageQueue創建。

構造方法

MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit(); } 復制代碼

mQuitAllowed表示這個消息隊列能不能退出,除了主線程外,其他線程的消息隊列都是可以退出的。
mPtr保存了native方法nativeInit()方法的返回值,這個值我們后面會用到,先看下nativeInit()做了什么吧。

nativeInit調用的是android_os_MessageQueue.cpp里面的方法

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); } 復制代碼

從上面的代碼我們知道,nativeInit創建了一個NativeMessageQueue對象,并將它的指針強轉成了Java中的long類型保存在mPtr中,我們可以這樣理解,MessageQueue.java中保存了一份NativeMessageQueue的指針,在需要的時候,mPtr可以傳給native方法并轉換成NativeMessageQueue對象。

NativeMessageQueue的構造方法

NativeMessageQueue::NativeMessageQueue() :mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {mLooper = Looper::getForThread();if (mLooper == NULL) {mLooper = new Looper(false);Looper::setForThread(mLooper);} } 復制代碼

上面的代碼的作用很簡單,如果ThreadLocal中有Looper對象就返回。如果沒有就new一個Looper對象,并將其保存在ThreadLocal中。

Looper.cpp的構造函數

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(); } 復制代碼

上面的代碼有一個關鍵的方法eventfd,這個具體的作用可參看Linux進程間通信-eventfd,簡單講的話這個就是linux進程間通信的一種方式,Linux內核空間維護了一個64位的計數器,可以一個進程調用write寫入一個數,另一個進程調用read讀出來。

最后一個函數

void Looper::rebuildEpollLocked() {// Close old epoll instance if we have one.if (mEpollFd >= 0) {close(mEpollFd);}// Allocate the new epoll instance and register the wake pipe.mEpollFd = epoll_create(EPOLL_SIZE_HINT);LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));struct epoll_event eventItem;memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field unioneventItem.events = EPOLLIN;eventItem.data.fd = mWakeEventFd;int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",strerror(errno));for (size_t i = 0; i < mRequests.size(); i++) {const Request& request = mRequests.valueAt(i);struct epoll_event eventItem;request.initEventItem(&eventItem);int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);if (epollResult < 0) {ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",request.fd, strerror(errno));}} } 復制代碼

這個里面也有兩個關鍵的方法epoll_create和epoll_ctl,epoll的具體講解可以看這個。簡單說就是epoll可以監控很多個文件描述符,并注冊想要監控的事件。mWakeEventFd是之前我們創建的那個eventfd的文件描述符,EPOLLIN表示寫事件。所以

int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem) 復制代碼

這行代碼的含義就是當eventfd有寫入事件時觸發。

sendMessage的流程

我們先來看一下Handler中sendMessage大致的時序圖

  • Handler調用自身的方法
  • 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); } 復制代碼
  • MessageQueue調用enqueueMessage方法
  • boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// 第一個消息;需要喚醒Loopermsg.next = p;mMessages = msg;needWake = mBlocked;} else {// 按照時間排序找到合適的插入點,這種情況通常不需要喚醒Looper,但是如果同步分隔欄且是第一個異步消息就需要喚醒LooperneedWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;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.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true; } 復制代碼

    上面代碼是Message加入隊列的過程,Message入隊過程并不像我們Demo里寫的那樣先進先出的,而是按照執行時間進行排序,如果這個Message是隊列里面的第一個Message,則需要喚醒Looper。我們假設我們發送的Message就是第一個,那么就會觸發nativeWake方法,傳入的參數就是我們在MessageQueue構造函數中保存的指針mPtr,這個指針指向NativeMessageQueue。

  • android_os_MessageQueue.cpp中的android_os_MessageQueue_nativeWake
  • static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);nativeMessageQueue->wake(); } 復制代碼
  • NativeMessageQueue調用wake方法
  • void NativeMessageQueue::wake() {mLooper->wake(); } 復制代碼
  • Looper.cpp調用wake方法
  • void Looper::wake() { #if DEBUG_POLL_AND_WAKEALOGD("%p ~ wake", this); #endifuint64_t inc = 1;// 向eventfd中寫入1ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));if (nWrite != sizeof(uint64_t)) {if (errno != EAGAIN) {LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",mWakeEventFd, strerror(errno));}} } 復制代碼

    mWakeEventFd就是在Looper的構造方法中創建的eventfd的文件描述符。上述代碼的作用就是向eventfd中寫入一個1。還記得Looper構造方法中的epoll嗎?epoll正在監聽eventfd的寫事件,現在已經觸發了。觸發了什么事呢?我們接著往下分析。

    handleMessage的流程

    我們先來看一下Handler中handleMessage大致的時序圖

  • Looper中的loop
  • public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();for (;;) {//消息隊列為空的時候會阻塞Message msg = queue.next(); if (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;if (traceTag != 0) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}try {msg.target.dispatchMessage(msg);} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}msg.recycleUnchecked();} } 復制代碼

    無限循環取Message,取到Message后調用msg.target.dispatchMessage(msg)分發給對應的Handler處理,看著似乎跟我們Demo里是一樣的,但關鍵點就在于

    Message msg = queue.next(); 復制代碼

    這個操作是會阻塞的,還記得Looper.cpp里面的wake有個喚醒操作嗎?這個喚醒操作就是為了喚醒queue.next()的。

  • MessageQueue的next方法
  • Message next() {final long ptr = mPtr;if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}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;}// Process the quit message now that all pending messages have been handled.if (mQuitting) {dispose();return null;}if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// No idle handlers to run. Loop and wait some more.mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// 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 handlerboolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}pendingIdleHandlerCount = 0;nextPollTimeoutMillis = 0;} } 復制代碼

    當MessageQueue里面沒有消息時nativePollOnce(ptr, nextPollTimeoutMillis)會阻塞

  • android_os_MessageQueue.cpp中的nativePollOnce
  • 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); } 復制代碼
  • 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;} } 復制代碼
  • Looper.cpp中的pollOnce方法
  • int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {int result = 0;for (;;) {while (mResponseIndex < mResponses.size()) {const Response& response = mResponses.itemAt(mResponseIndex++);int ident = response.request.ident;if (ident >= 0) {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;return ident;}}if (result != 0) {if (outFd != NULL) *outFd = 0;if (outEvents != NULL) *outEvents = 0;if (outData != NULL) *outData = NULL;return result;}result = pollInner(timeoutMillis);} } 復制代碼
  • Looper.cpp中的pollInner方法
  • int Looper::pollInner(int timeoutMillis) {// Poll.int result = POLL_WAKE;mResponses.clear();mResponseIndex = 0;// We are about to idle.mPolling = true;struct epoll_event eventItems[EPOLL_MAX_EVENTS];// 等待eventfd中的寫事件觸發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 == mWakeEventFd) {if (epollEvents & EPOLLIN) {awoken();} else {ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);}}}return result; } 復制代碼

    pollInner中代碼很長,我刪去了一些不相關的代碼,主要就是這行代碼

    struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); 復制代碼

    epoll_wait會一直等待eventfd中的寫事件觸發,如果沒有就會阻塞。

  • 最后的awoken
  • void Looper::awoken() {uint64_t counter;TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t))); } 復制代碼

    讀取eventfd中保存的數據,read操作后eventfd會置0,重新陷入阻塞

    總結

  • Looper調用prepare方法創建MessageQueue
  • MessageQueue創建的時候同時調用jni方法創建了eventfd和epoll,epoll監聽eventfd上的寫事件。
  • Looper調用loop方法進入循環。
  • 執行到nativePollOnce時,由于eventfd計數器的值為0陷入阻塞。
  • Handler調用sendMessage發送消息。
  • message加入到MessageQueue,同時執行nativeWake方法向eventfd寫入一個數字1。
  • nativePollOnce阻塞解除,分發Message給對應的Handler。
  • 讀取eventfd中的數據,eventfd置0。nativePollOnce繼續阻塞。
  • 轉載于:https://juejin.im/post/5ad61225f265da237f1ecf0a

    總結

    以上是生活随笔為你收集整理的Handler源码剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。