消息机制主要用于多线程的通信,在android开发中最常见的场景就是,在子线程中做耗时操作,比如下载图片等等,操作完成后需要在主线程中更新ui,这时就需要用到消息机制来完成子线程与主线程的通信。Android规定访问UI只能在主线程中进行,如果在子线程中访问UI就会发生异常,这是因为ViewRootImpl对UI的操作做了验证,这个验证是在ViewRootImpl的checkThread()方法中完成的。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
系统提供Handler主要原因是解决在子线程中无法更新UI的矛盾。那么刚才提到为什么不允许子线程中访问UI呢?那是因为Android的UI控件并不是线程安全的。如果存在多线程的并发访问可能会导致UI控件处于不可预期的状态。那么系统为什么不对UI控件加锁呢。一方面加锁使逻辑更复杂,二来要进行锁判断,影响效率,阻塞了其它线程,所以简单高效的模型就是单线程。
Handler消息机制常见场景如下简单示例:
public class MainActivity extends Activity {
private TextView tvText;
//定义Handler
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//获取子线程中发送过来的消息,然后在主线程中更新ui
tvText.setText(msg.obj.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.tv_text);
//创建子线程,启动子线程任务
new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = Message.obtain();
msg.obj = Thread.currentThread().getName();
//子线程任务执行完毕后,将信息封装成消息Message,通过Handler发送回主线程
mHandler.sendMessage(msg);
}
}.start();
}
}
Android的消息机制其实主要是指Handler的运行机制,而Handler的运行又需要底层MessageQueue和Looper的支撑。MessageQueue中文翻译是指消息队列,顾名思义,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作。虽然叫消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。Looper中文翻译为循环,由于MessageQueue只是提供了一个消息的存储,它并不能去处理消息,Looper就填补了这个功能,Looper会以无限循环的方式去查找是否有新消息,如果有的话就处理否则就会一直等待着。Looper中还要一个特殊的概念ThreadLocal,用它可以在不同的线程中存储数据,互不干扰,解决多线程程序并发问题。那么在Handler如何获取到当前线程的Looper呢。就是通过这个ThreadLocal,ThreadLocal可以在不同线程中互不干扰的存储和读取数据,通过ThreadLocal就可以轻松获取到每个线程的Looper。线程默认是没有Looper的,我们在子线程使用Handler必须先创建Looper否则会发生异常。至于主线程为什么可以直接使用Handler呢?那是因为主线程在入口的main方法中就已经帮我们创建了一个Looper对象,并开启了一个Looper循环,帮我构建好了这样一个消息消息循环的环境。
那么Handler为什么能切换线程?
一句话总结就是:Handler是线程间共享的一个对象,而Looper则是每个线程私有的。Handler是共享的意思是,在需要切换的2个线程中都能访问我们创建的Handler对象,比如能调用Handler的sendMessage方法。
下面我们重点介绍一点android Handler消息机制的几个核心类及源码分析:Looper,Handler,Message
Looper的工作原理:
Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。其中涉及到MessageQueue,存放消息的地方,MessageQueue主要包含两个操作:插入和读取。读取本身伴随着删除操作,对应着两个方法分别是enqueueMessage()和next()。enqueueMessage是插入一条消息到消息队列中,next是取出一条信息并将其从队列移除。MQ被封装到Looper里面了,我们不会直接与MQ打交道。在子线程中需要我们手动创建Looper对象
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// 开始循环处理消息队列
Looper.loop();
}
}
通过 Looper.prepare(); Looper.loop(); 这两个方法就将此线程变成了Looper线程了,看看这两个方法的源码
//每个线程中的Looper对象其实都是一个ThreadLocal,存放在sThreadLocal中
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//looper消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
//一个线程有且仅有一个looper
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static void loop() {
//获取当前线程looper
final Looper me = myLooper();
if (me == null) {
//如果当前线程没有将looper对象定义为ThreadLocal,即没执行looper.prepare 将抛出异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取消息队列
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//开启无限循环
for (;;) {
Message msg = queue.next(); // might block
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 logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
将真正的处理工作交给message的target,即Handler
msg.target.dispatchMessage(msg);
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();
}
}
Handler的工作原理:
handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。一个线程可以有多个Handler,但是只能有一个Looper!
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());
}
}
//默认将关联当前线程的looper
mLooper = Looper.myLooper();
// looper不能为空,即该默认的构造方法只能在looper线程中使用
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler发送消息
有了handler之后,我们就可以使用 post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long)和 sendMessageDelayed(Message, long)这些方法向MQ上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了。
// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
public final boolean post(Runnable r)
{
// 注意getPostMessage(r)将runnable封装成message
return sendMessageDelayed(getPostMessage(r), 0);
}
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain(); //得到空的message
m.callback = r; //将runnable设为message的callback,
return m;
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this; // message的target必须设为该handler!
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
Handler处理消息
说完了消息的发送,再来看下handler如何处理消息。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,
处理消息,该方法由looper调用 ,msg.target.dispatchMessage(msg);
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果message设置了callback,即runnable消息,处理callback!
handleCallback(msg);
} else {
if (mCallback != null) {
// 如果handler本身设置了callback,则执行callback
if (mCallback.handleMessage(msg)) {
return;
}
}
// 如果message没有callback,则调用handler的钩子方法handleMessage
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public interface Callback {
public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
Message 解析
Message封装了任务携带的信息和处理该任务的handler。尽管Message有public的默认构造方法,但是应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。如果message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存,擅用message.what来标识信息,以便用不同方式处理message,下面看一下obtain的源码
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
创建消息的时候会带着target,即此message是哪个Handler处理
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
总结
现在重新回顾一下前面举例的子线程处理耗时,然后更新主线程更新ui的过程:完整的走一次消息处理机制的流程
定义一个handler 实现handleMessage方法 处理子线程返回的消息 在主线程中开启一个子线程,由于是在主线程中所以无需创建Looper对象,如果是在其他线程中需手动创建。在子线程执行完毕后,定义一个message
Message msg = Message.obtain(); 并在消息中带了target 即处理此message的handler,然后将message 通过 mHandler.sendMessage(msg); 发送到MessageQueue中,looper处理MessageQueue,然后通过 msg.target.dispatchMessage(msg); 将消息交由handler的dispatchMessage方法处理,再回调到handleMessage,handleMessage 将message在主线程中更新ui,这个消息处理过程就完成了。