重拾Handler

说到Handler,相信学过Android的同学都能随口说出:这家伙是线程间通信的一种机制。但是,平时用来发送消息的方式有哪些?Handler是什么时候和Looper关联的?又是怎么关联的?消息在子线程中发出,在UI线程为什么能获取的到?这这这。。。太难了
在这里插入图片描述
Handler不能单独使用,内部配合着Message,Looper,MessageQueue以及ThreadLocal,这几个平时比较少直接操作,但却是Handler机制中必不可少的一部分。

本文采用Android SDK 29版本
在这里插入图片描述
在这里插入图片描述

首先从知道它们分别是什么开始吧

  • Handler:发送和处理消息。
  • Message:他就是我们常说的消息的封装体。
  • MessageQueue:负责承载Message消息,消息来时插入,Looper来要时取出。
  • Looper:负责从MessageQueue中取出消息,并交给Handler处理。
  • ThreadLocal:通过搜狗百科查询到,它是JDK1.2为解决多线程并发提供的一种方案,内部维护着一个Map,并不是Thead。这里的主要作用是在指定的线程储存数据(描述的可能不正确)。

平时的常规操作,分为Handler#sendMessageHandler#post
在这里插入图片描述

Handler消息发送

Handler#sendXxx系列方法
在这里插入图片描述
Handler#postXxx系列方法
在这里插入图片描述
无论是send系列还是post系列方法,最终都走到了Handler#sendMessageAtTime,而Message进入MessageQueue则是在MessageQueue#enqueueMessage

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

如果当前队列为空,则将消息作为队列头插入;否则在中间插入,并且插入依据是根据when(创建时间)

   boolean enqueueMessage(Message msg, long when) {
        //忽略一些判断代码
        synchronized (this) {
        //如果线程已经停止,则释放Message资源
            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 {
                // 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;
                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.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

Handler和Looper

从上面写的代码来看,我们知道在调用post或send的时候,最终会把Message加入到队列中,但是没有看到Looper的身影啊,其实它是在Handler的构造方法中和Handler关联起来的。

public Handler(@Nullable Callback callback, boolean async) {
	//忽略若干代码
	//在这里获取当前线程的Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    //当前线程持有一个MessageQueue
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Looper#myLooper中可以看到,从ThreadLocal中获取到当前线程的Looper。

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

但是有get,那么就必然在哪里有set,通过搜索ThreadLocal#set方法,可以看到在ThreadLocal#prepare中将Looper set到了ThreadLocal中


public static void prepare() {
   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));
}
public static void prepareMainLooper() {
   prepare(false);
   synchronized (Looper.class) {
      if (sMainLooper != null) {
          throw new IllegalStateException("The main Looper has already been prepared.");
      }
      sMainLooper = myLooper();
   }
}

但是ThreadLocal#prepare方法我找遍了Handler、Looper、Message、MessageQueue和ThreadLocal都找不到,原来它是在应用启动的时候,ActivityThread#main中调用了

	public static void main(String[] args) {
		//忽略若干
        Looper.prepareMainLooper();
		//忽略若干
        Looper.loop();
		//忽略若干
    }

到此,可以得出结论,Handler与Looper是在实例化一个Handler的时候关联起来的,并且从ThreadLocal中获取当前线程的Looper,并且在Looper#loop中不断轮训,从MessageQueue取出Message

Looper与Message、MessageQueue

从前面的分析中我们知道,Handler通过post或send方法,最终调用了MessageQueue#enqueueMessage方法处理消息的存入和取出。而Looper#loop则不断轮训队列,将队列中的消息发射到Handler中。

 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;
        // 忽略若干细节
        for (;;) {
        	//从MessageQueue中取出下一条Message
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //...
            try {
            	//target指的是Handler,通过dispatchMessage将消息分发
                msg.target.dispatchMessage(msg);
            //...
            } catch (Exception exception) {
            //...
            } finally {
            //...
            }
            //...
            // 释放Message
            msg.recycleUnchecked();
        }
    }

可以看到,在循环中不断通过MessageQueue#next取出消息,然后通过Handler#dispatchMessage分发出去,分别看看这两个方法。

Message next() {
        //忽略...
        for (;;) {
        	//忽略...
        	//Linux底层实现阻塞/唤醒的select、poll和epoll机制
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                Message prevMsg = null;
                Message msg = mMessages;
        		//忽略...
                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) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
                //忽略...
            }
    }
/**
 * Handle system messages here.
 */
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

到了这里,是不是看到了我们熟悉的handleMessage;一般情况下,sendXxx方法,都会执行到handleMessage,然后交由我们去处理,而postXxx一般会带Runnable进来到,所以会执行到run方法。这里分析基本就结束了。

当然其中UI线程中执行Looper#loop为什么不卡死涉及到了Linux底层相关机制,本人水平有限就不深入研究了。

最后再总结一下:

  • 初始化Handler的时候,同时会创建一个Looper,并与当前线程绑定。
  • Handler发消息到MessageQueue中,在enqueueMessage中将消息插入队列。
  • Looper的loop()从队列中不断循环取出,并且交给target.dispatchMessage()。
  • Handler#dispatchMessage中判断是否存在Runnable,存在则回调到Runnable#run方法,否则直接回调Handler#handlerMessage
  • 需要注意的是,一个线程只存在一个Looper,一个Looper只去轮询一个队列,所以一个线程只存在一个队列

Handler其实在Android体系里面还是非常重要的,基本贯穿了整个框架,很有必要深入学习。

欢迎关注我的个人微信公众号,【优了个秀】和你每天进步一点点
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值