Android消息机制的Handler
Handler是Android消息机制的核心,但是要分析Handler的原理离不开MessageQueue和Looper。所以三者要一起分析。待解决问题:为什么在子线程中执行new Handler()会抛出异常?
先看图
这是描述Android消息机制的一张图,每个线程有一个Looper和一个MessageQueue,以及若干个Handler。其中MessageQueue存放了一系列Message的队列,而每个Handler通过sendMessage
方法发送一个Message到MessageQueue中,并且这个Message持有发送它的Handler的引用。Looper通过Looper.loop()
方法不断地从MessageQueue中取出Message,并将取出来的Message交给它持有的Handler对象,这个Handler对象通过handleMessage
方法处理这个Message。
关键在于,Handler的handleMessage
一定会在创建它的线程的上下文中被调用,但是它的sendMessage
可以在其他线程中被调用,因此通过Handler可以实现跨线程通信。
首先举一个Android消息机制的例子来引入主题。
Android中常用handler更新UI,在主线程实例化handler,实现它的handleMessage()
方法,为UI控件添加一个点击事件,在子线程中实例化一个Message对象,然后用handler发送它,handler在主线程中接收这个Message对象并更新UI。
public class HandlerActivity extends BaseActivity implements View.OnClickListener { private Button mBtnChangeUI; private Handler mHandler; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.module_activity_handler); mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case 0: mBtnChangeUI.setText(new StringBuilder(mBtnChangeUI.getText()).reverse()); return true; } return false; } }); mBtnChangeUI = findViewById(R.id.btn_changeUI); mBtnChangeUI.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_changeUI: new Thread(new Runnable() { @Override public void run() { mHandler.sendEmptyMessage(0); } }).start(); break; } }}
让我们看看这里发生了什么。首先我们在UI线程里创建了mHandler,并实现了它的handleMessage()
方法,然后实现点击事件,当Button被点击时,创建一个字线程,在子线程里用mHandler发送一个Message,随后倒置Button上的字符串。
结合上面的图,在主线程中创建mHandler,于是mHandler是主线程的,在子线程中发送一个Message,将其放入MessageQueue,这个MessageQueue应该和mHandler在同一个线程,所以也是主线程的,与mHandler同样位于主线程的Looper不断的从MessageQueue中取出Message,并将其交给Message持有的Handler也就是mHandler处理,mHandler在handleMessage
中处理Message,将Button上的字符串倒置。
在代码中看不到Looper和MessageQueue,因此要查看源码(注:为了简洁,省略了部分源码)。
首先,看Handler的创建过程:
Handler的构造函数
Handler有多个构造函数,这里使用的是
public Handler(Callback callback) { this(callback, false);}public Handler(Callback callback, boolean async) { // ... mLooper = Looper.myLooper();// ... mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;}
可以看到这里出现了Looper,mLooper是Handler的一个实例变量,这里的mQueue也是实例变量,而且是MessageQueue类型的,因此Handler持有Looper和MessageQueue的引用。同时Looper也持有MessageQueue的引用。如图所示:
Handler一共有七个构造函数:
public Handler();public Handler(boolean async);public Handler(Callback callback);public Handler(Callback callback, boolean async);public Handler(Looper looper);public Handler(Looper looper, Callback callback);public Handler(Looper looper, Callback callback, boolean async);
但是它们之间会互相调用,因此最终起作用的只有两个构造函数:
public Handler(Callback callback, boolean async);
和public Handler(Looper looper, Callback, callback, boolean async);
前者通过Looper.myLooper()
获取Looper,后者手动指定Looper。MessageQueue和Looper是绑定的,制定了Looper也就指定了MessageQueue。
观察Handler的两类方法:发送消息
和处理消息
Handler发送消息的方法
发送消息又有七个方法:
public final boolean sendEmptyMessage(int what);public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis);public final boolean sendEmptyMessageDelayed(int what, long delayMillis)public final boolean sendMessage(Message msg);public final boolean sendMessageAtFrontOfQueue(Message msg);public boolean sendMessageAtTime(Message msg, long uptimeMillis);public final boolean sendMessageDelayed(Message msg, long delayMillis);
同样的,它们之间互相调用,最终起作用的是两个方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { return false; } return enqueueMessage(queue, msg, uptimeMillis);}public final boolean sendMessageAtFrontOfQueue(Message msg) { MessageQueue queue = mQueue; if (queue == null) { return false; } return enqueueMessage(queue, msg, 0);}
我们发现发送消息
最终就是调用了enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
这个方法,顾名思义,这是一个Message入队的方法,它的代码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}
这里,Message获得了Handler的引用,message.target = handler
。而queue是一个MessageQueue变量。这正是——Message被其持有的Handler发送,插入到MessageQueue。
MessageQueue的enqueueMessage
方法后面再看。记录1⃣️
Handler处理消息的方法
/** * Handle system messages here. */public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}private static void handleCallback(Message message) { message.callback.run();}public interface Callback { public boolean handleMessage(Message msg);}public void handleMessage(Message msg) {}
除了常用的覆写handleMessage
接口的方法实现处理消息的方法之外,还有两种方法,使用Message自带的callback,或者传入一个Callback实例。代码很清晰。传入Callback实例的方法是采用Handler构造方法中的public Handler(Callback callback, boolean async);
或public Handler(Looper looper, Callback callback, boolean async);
。
Message的callback变量后面再看。记录2⃣️
综上,Handler的常用方法讲完了,现在解决记录1⃣️。分析MessageQueue的源码。
1⃣️MessageQueue的enqueueMessage
方法。
MessageQueue
Android的每个线程可以持有一个Looper对象和一个MessageQueue对象,Looper不停地从MessageQueue里取出Message,并将Message交给Message持有的Handler对象来处理,当MessageQueue为空时Looper阻塞。而Message是由其持有的Handler对象插入MessageQueue的。Handler持有它被创建的线程的上下文,并且可以被多线程共享。
MessageQueue是由一个单链表的数据结构实现的,主要包含两个操作:插入和读取,对应的方法为enqueueMessage和next,enqueueMessage的作用是插入一条Message,next的作用是取出一条Message并将其从MessageQueue中移除,先发出的Message靠近队列的首部,现在看这两个方法的源码:
// 插入msg,指定其在when(ms)的延时后触发boolean enqueueMessage(Message msg, long when) { //... // 单链表插入,通过同步来防止多线程冲突 synchronized (this) { //... msg.when = when; Message p = mMessages; boolean needWake;//需要唤醒的标识 if (p == null || when == 0 || when < p.when) { // 消息队列为空 || msg是立即触发的消息 || msg比队列首部的消息先触发,则将其插入到队列首部 msg.next = p; mMessages = msg; needWake = mBlocked;// } else { // 消息队列不为空 && msg比队列首部的消息晚触发 && p不持有Handler && msg是异步消息 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; // 根据触发的时间将msg插入到合适的位置 prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true;}
needWake这个变量的用法还不明朗,在省略的源码里也有一些不清楚用法的变量,Message的同步/异步是什么,inUse表示是什么,等等。但是这个方法的主要功能是明显的,向队列插入Message,MessageQueue是单链表实现的,其中的Message按照触发的时间when排序,先触发的排在前面。有入队,就有出队,出队方法是next
。
Message next() {//... for (;;) { //... synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; // MessageQueue不为空 && 队首的Message不持有Handler if (msg != null && msg.target == null) { // 找到MessageQueue中第一个不为空 && 同步的Message do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } // if (msg != null) { // 还没到Message触发的时间 if (now < msg.when) { //... } else {//... // 从队列中移除Message if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; //... return msg; } } else { // No more messages. //... } } }}
next
是一个循环函数,在MessageQueue里找到符合条件的Message(条件很复杂),将其从MessageQueue中移除并返回。
接下来分析Looper。
Looper
Looper不停地查看MessageQueue中是否有Message,这是在它的loop
方法中完成的,因此loop
是它的核心方法,从这个方法开始看
public static void loop() { final Looper me = myLooper(); //... 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 { msg.target.dispatchMessage(msg);// 使用Message持有的Handler处理事件。 //... } finally { //... } //... }}
可以看到,loop
是一个循环函数,从MessageQueue中不断地取出Message并处理,当MessageQueue为空时退出循环。照例,MessageQueue是与Looper绑定的,通过myLooper
方法获取Looper。查看myLooper
方法:
public static @Nullable Looper myLooper() { return sThreadLocal.get();}static final ThreadLocal sThreadLocal = new ThreadLocal();
我们发现这里用到了ThreadLocal变量,也就是说每个线程都有一个线程独立的Looper,而他是在Looper.prepare()
方法里获取到的。
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));}
可以看到每个线程只能有一个Looper。
到这里,我们可以解决开头的问题了——为什么在子线程中执行new Handler()会抛出异常?
在子线程中执行new Handler(),Handler()中需要调用myLooper()
获取Looper,而此时Looper是空值,因此抛出异常。
public Handler() { this(null, false);}public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //...}
那么,为什么在主线程中执行new Handler()不会抛出异常呢?回到Looper的源码,可以发现prepare
方法被prepareMainLooper
方法调用了,而prepareMainLooper
方法又被ActivityThread.main
方法调用了。
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}public static void main(String[] args) { //... Looper.prepareMainLooper(); //... Looper.loop(); //...}
因此,在主线程初始化的时候就已经创建了Looper,所以执行new Handler()不会抛出异常,在子线程中就需要手动调用Looper.prepare()
来创建了。主线程的Looper名为sMainLooper,是一个静态私有变量,使用共有静态方法getMainLooper
就可以获取到它,因此在子线程中也可以手动获取主线程的Looper。
顺便看看Looper的构造函数
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
Looper在实例化的时候会创建自己的MessageQueue,并获取当前线程的引用。因此每个Looper都有一个MessageQueue。Looper使用单例设计,每个线程只能有一个Looper。子线程中的Looper使用ThreadLocal存储,对其处理时不需要做同步处理,主线程中的Looper是一个类变量,对其处理时需要做同步处理。
Looper不停地查看MessageQueue中是否有Message,如果有新消息就处理,否则阻塞。通过Looper.prepare()
创建Looper。
综上所述,Handler-MessageQueue-Looper的关系就讲完了。再回顾一下Android消息机制是如何实现的:
-
每个线程中都至多只能持有一个Looper,每个Looper持有一个MessageQueue实例和当前线程的引用。主线程在初始化的时候自动调用
Looper.prepareMainLooper
创建了一个MainLooper,对于子线程,需要手动调用Looper.prepare
创建Looper。 -
每个线程可以持有多个Handler,Handler持有一个Looper的引用,可以为Handler指定Looper,也可以让Handler自行获取当前线程的Looper。
-
手动创建Message,使用Handler发送Message,Handler会调用MessageQueue的方法,将Message插入MessageQueue。
-
Looper.loop
,循环从MessageQueue取出Message,然后调用Handler.dispatchMessage()
来处理Message。
参考资料:
《Android开发艺术探索》
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用
- python list.sort()根据多个关键字排序的方法实现
- E/错误(3907): android.view.ViewRootImpl$CalledFromWrongThrea
- Android(安卓)CheckBox中设置padding无效问题解决方法
- android的Handler
- 安卓入门笔记之Activity
- Android四大基本组件介绍与生命周期
- Android(安卓)WiFiDirect 学习(二)——Service Discovery