Android Handler原理
前言
Handler消息處理機(jī)制在Android開發(fā)中起著舉足輕重的作用,我們有必要好好理解下其原理,先前我寫的一篇文章,感覺疏漏了好多東西,因此打算寫這篇文章,下面我們先從一個(gè)簡單的例子出發(fā)
一、日常使用
假設(shè)我們有這么一個(gè)需要,請(qǐng)求網(wǎng)絡(luò)然后將圖片展示出來,我們知道網(wǎng)絡(luò)請(qǐng)求是不允許在主線程執(zhí)行的,而UI是不能在子線程(具體是不允許在非創(chuàng)建UI的原始線程)更新的,因此我們需要在子線程請(qǐng)求網(wǎng)絡(luò)獲得了數(shù)據(jù)以后再切換回主線程更新UI,這個(gè)例子中Handler就是起著切換線程的作用,下面的代碼演示了這個(gè)例子
class MainActivity : AppCompatActivity() {private lateinit var mImageView: ImageViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)mImageView = findViewById(R.id.iv)loadImage()}private fun loadImage() {Thread {val url = URL("https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg")val conn = url.openConnection()val bitmap = BitmapFactory.decodeStream(conn.inputStream)runOnUiThread {mImageView.setImageBitmap(bitmap)}}.start()} } 復(fù)制代碼咦!,說好的Handler去哪了?這里的runOnUIThread方法內(nèi)部實(shí)現(xiàn)其實(shí)就是利用了Handler,我們來看看它的源碼
public final void runOnUiThread(Runnable action) {if (Thread.currentThread() != mUiThread) {mHandler.post(action);} else {action.run();} } 復(fù)制代碼該方法首先判斷了當(dāng)前線程是否是主線程,如果不是主線程就調(diào)用mHandler.post(),如果當(dāng)前線程就是主線程就直接運(yùn)行,下面我們來分析看看Handler的原理
二、Handler原理
要想分析Handler的原理,我們先從Handler的創(chuàng)建過程開始分析
Handler的創(chuàng)建
Activity的這個(gè)mHandler是怎么來的呢?原來mHandler是Activity的成員變量,在Activity實(shí)例創(chuàng)建的時(shí)候就創(chuàng)建了
final Handler mHandler = new Handler(); 復(fù)制代碼接著看看Handler的構(gòu)造方法
public Handler() {this(null, false); } public Handler(Callback callback, boolean async) {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());}}mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;// 代表發(fā)送的消息是否是異步的mAsynchronous = async; } 復(fù)制代碼首先判斷了該Handler派生類是否是非靜態(tài)內(nèi)部類,如果是的話就打印出日志提示可能導(dǎo)致內(nèi)存泄露,然后調(diào)用了Looper.myLooper獲取到當(dāng)前線程的Looper對(duì)象,如果當(dāng)前線程沒有Looper就會(huì)拋出異常,最后將Looper中的MessageQueue對(duì)象賦值給mQueue,callback賦值給mCallback,aync賦值給mAsynchronous,我們來看看Looper.myLooper做了些什么
public static Looper myLooper() {return sThreadLocal.get(); } 復(fù)制代碼從ThreadLocal里面去Looper,那么是在哪里把Looper設(shè)置到ThreadLocal里面去的呢?其實(shí)Looper提供了prepare方法來創(chuàng)建當(dāng)前線程的Looper,我們來看看代碼
public static void prepare() {// 表示允許退出循環(huán)prepare(true); } private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed)); } 復(fù)制代碼只有在當(dāng)前線程拿不到Looper的時(shí)候才會(huì)去創(chuàng)建Looper對(duì)象并將其設(shè)置到ThreadLocal中去,不然就拋出異常說一個(gè)線程只能擁有一個(gè)Looper,繼續(xù)看看Looper的構(gòu)造方法
// 這里的quitAllowed是true private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread(); } 復(fù)制代碼又創(chuàng)建了一個(gè)MessageQueue對(duì)象,繼續(xù)看看它的構(gòu)造方法
// 這里的quitAllowed是true MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit(); } 復(fù)制代碼調(diào)用了一個(gè)Native方法就結(jié)束了,其實(shí)mPtr是NativeMessageQueue與MessageQueue之間的橋梁內(nèi)部會(huì)調(diào)用epoll.create()、epoll.ctl(),暫時(shí)不看native層代碼
源碼看到這里就會(huì)產(chǎn)生一個(gè)疑問,既然創(chuàng)建Handler的時(shí)候判斷了當(dāng)前線程的Looper是否為null,為null就會(huì)拋出異常,那么Activity的Handler是怎么創(chuàng)建成功的呢?其實(shí)在Activity實(shí)例創(chuàng)建前主線程就已經(jīng)有Looper對(duì)象了,這個(gè)得從ActivityThread開始說起。ActivityThread是一個(gè)應(yīng)用程序的入口里面有一個(gè)main方法,我們來看看
// 忽略其它代碼 public static void main(String[] args) {...Looper.prepareMainLooper();Looper.loop();... } 復(fù)制代碼Looper.loop()后面會(huì)講到先忽略,main方法內(nèi)部調(diào)用了Looper.prepareMainLooper()這個(gè)方法跟上面講到的Looper.prepare()有什么異同點(diǎn)呢?我們來看看它的源碼
public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();} } 復(fù)制代碼prepare方法前面已經(jīng)分析過了但是主線程是不允許退出的,所以傳入了false,后面判斷了如果sMainLooper不為空那么就拋出異常,至此主線程的Looper創(chuàng)建成功這也就解釋了為什么Activity中可以直接創(chuàng)建Handler,接著我們分析那個(gè)post方法干了些什么事情
Handler的消息發(fā)送
Handler提供了很多方法用于發(fā)送消息,比如以下幾種
- sendEmptyMessage(int what) 發(fā)送一個(gè)空消息,what用于判斷消息類別
- sendEmptyMessageDelayed(int what, long delayMillis) 發(fā)送一個(gè)空消息,延遲delayMillis毫秒執(zhí)行,what用于判斷消息類別
- sendEmptyMessageAtTime(int what, long uptimeMillis) 發(fā)送一個(gè)空消息,在uptimeMillis的時(shí)候執(zhí)行,what用于判斷消息類別
- sendMessageDelayed(Message msg, long delayMillis) 發(fā)送一個(gè)消息,延遲delayMillis毫秒執(zhí)行
- sendMessageAtTime(Message msg, long uptimeMillis) 發(fā)送一個(gè)消息,在uptimeMillis的時(shí)候執(zhí)行
- sendMessageAtFrontOfQueue(Message msg) 發(fā)送一個(gè)消息,該消息會(huì)排在消息隊(duì)列的隊(duì)首
- executeOrSendMessage(Message msg) 如果Handler中的Looper與當(dāng)前線程的Looper一致就直接分開消息,不然就發(fā)送一個(gè)消息
我們繼續(xù)著看post方法的實(shí)現(xiàn)
public final boolean post(Runnable r) {return sendMessageDelayed(getPostMessage(r), 0); } 復(fù)制代碼其實(shí)post方法內(nèi)部也就是發(fā)送了一個(gè)消息
private static Message getPostMessage(Runnable r) {// message內(nèi)部維護(hù)了一個(gè)Message鏈表,以達(dá)到復(fù)用的目的,記得不要直接newMessage m = Message.obtain();m.callback = r;return m; } 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); } 復(fù)制代碼最終調(diào)用到了sendMessageAtTime,其實(shí)幾乎所有發(fā)送消息的方法最終都會(huì)調(diào)用到該方法,繼續(xù)看enqueueMessage的實(shí)現(xiàn)
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis); } 復(fù)制代碼這里將本Handler的實(shí)例賦值給了msg.target,這個(gè)很重要以后會(huì)用到,然后判斷下當(dāng)然Handler是否是異步的,是的話就將消息設(shè)置成異步,我們這里不是異步的,接著繼續(xù)看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) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {needWake = 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;}if (needWake) {nativeWake(mPtr);}}return true; } 復(fù)制代碼該方法首先判斷了msg.target是否為空,這個(gè)我們剛才看到已經(jīng)設(shè)置了,然后判斷msg是否正在被使用,然后再判斷消息隊(duì)列是否已經(jīng)退出了,如果已經(jīng)退出了就將msg回收并拋出個(gè)異常,下面那個(gè)同步代碼塊其實(shí)處理的邏輯就是將msg放入到消息隊(duì)列中去,插入過程分為以下兩步,至于needWake是用于判斷是否要喚醒處于nativePollOnce而阻塞的Message.next方法
- 如果滿足p == null || when == 0 || when < p.when其實(shí)也就是如果消息隊(duì)列的頭指針為空,或者當(dāng)前消息的執(zhí)行時(shí)間為0,或者當(dāng)前消息的執(zhí)行時(shí)間先與消息隊(duì)列隊(duì)首的執(zhí)行時(shí)間,那么將當(dāng)前msg當(dāng)做頭指針
- 如果不滿足第一種情況就根據(jù)當(dāng)前msg.when決定插入的位置
現(xiàn)在已經(jīng)將消息放到的消息隊(duì)列中,但是什么時(shí)候這個(gè)消息才能得到執(zhí)行呢?這就要看看前面跳過的ActivityThread的main方法中的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;boolean slowDeliveryDetected = false;for (;;) {Message msg = queue.next(); // might blockif (msg == null) {return;}final Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;if (thresholdOverride > 0) {slowDispatchThresholdMs = thresholdOverride;slowDeliveryThresholdMs = thresholdOverride;}final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);final boolean needStartTime = logSlowDelivery || logSlowDispatch;final boolean needEndTime = logSlowDispatch;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;final long dispatchEnd;try {msg.target.dispatchMessage(msg);dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logSlowDelivery) {if (slowDeliveryDetected) {if ((dispatchStart - msg.when) <= 10) {Slog.w(TAG, "Drained");slowDeliveryDetected = false;}} else {if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",msg)) {// Once we write a slow delivery log, suppress until the queue drains.slowDeliveryDetected = true;}}}if (logSlowDispatch) {showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}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();} } 復(fù)制代碼這個(gè)代碼有點(diǎn)長,主要流程如下
- 首先判斷一下當(dāng)前線程是否包含Looper不包含就拋出異常
- 調(diào)用MessageQueue的next方法獲取Message,如果返回了null,標(biāo)志了MessageQueue已經(jīng)退出了,所以Looper也要退出
- 獲取Looper中的mLogging用于打印日志,我們可以通過setMessageLogging進(jìn)行設(shè)置,設(shè)置后每次收到消息和消息處理完畢都會(huì)有日志我們可以根據(jù)這些日志分析ANR是由于處理哪個(gè)消息超時(shí)造成的
- 設(shè)置慢分發(fā)時(shí)間和慢交付時(shí)間,可以通過adb進(jìn)行設(shè)置,慢分發(fā)時(shí)間表示如果這個(gè)消息的實(shí)際執(zhí)行時(shí)間比其設(shè)置的slowDeliveryThresholdMs要長就會(huì)打印警告日志,慢交付時(shí)間表示這個(gè)消息從消息隊(duì)列取出時(shí)間比其設(shè)置的when超過slowDispatchThresholdMs就會(huì)打印警告日志
- 記錄開始分發(fā)的時(shí)間
- 調(diào)用msg.target.dispatchMessage進(jìn)行分發(fā)消息,其中msg.target就是一個(gè)Handler實(shí)例,上文說到過的
- 記錄結(jié)束分發(fā)的時(shí)間
- 根據(jù)實(shí)際情況打印日志
- 回收msg
loop方法調(diào)用queue.next取出消息,我們來看看該方法的實(shí)現(xiàn)
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) {final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {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;}if (mQuitting) {dispose();return null;}if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}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;} } 復(fù)制代碼該方法主要流程如下
- 調(diào)用nativePollOnce,阻塞等待下一個(gè)可執(zhí)行消息,該方法離開阻塞
- 判斷第一個(gè)消息的target是否為空,如果不為空表示是一個(gè)普通的消息,如果為空則表示是一個(gè)同步屏障消息(在屏幕刷新的時(shí)候會(huì)發(fā)送),遍歷消息隊(duì)列找到第一個(gè)異步消息賦值給msg
- 判斷msg是否為空,如果為空那么進(jìn)行無超時(shí)的等待,直到被喚醒
- 判斷msg是否到了執(zhí)行時(shí)間,如果不到就執(zhí)行阻塞等待msg.when - now,如果已經(jīng)到了就將該消息返回
- 如果消息隊(duì)列已經(jīng)退出就返回null
拿到了消息以后就調(diào)用了handler.dispatchMessage我們來看看其實(shí)現(xiàn)
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);} } 復(fù)制代碼首先判斷是否該Message是否設(shè)置了callBack,設(shè)置了就直接運(yùn)行,然后判斷Handler是否設(shè)置了callBack,設(shè)置了就調(diào)用callback.handleMessage如果返回false,繼續(xù)調(diào)用handleMessage
三、總結(jié)
- Handler的作用就是把想要執(zhí)行的操作從當(dāng)前線程切換到Handler中Looper所在的線程進(jìn)行執(zhí)行
- 每次通過Handler發(fā)送消息其實(shí)就是把消息插入到了消息隊(duì)列中,然后根據(jù)情況判斷是否要喚醒處于調(diào)用nativePollOnce阻塞狀態(tài)的線程
轉(zhuǎn)載于:https://juejin.im/post/5c9d846ff265da60d95fd58e
總結(jié)
以上是生活随笔為你收集整理的Android Handler原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jQuey基础思维导图梳理1
- 下一篇: 【软件工程】技术规格说明书