1、概述
Android的线程通信机制主要是指Handler的运行机制,而Handler的运行又离不开MessageQueue和Looper的支持,由这三个构建了Android的线程通信机制。
Handler负责投递消息和处理消息,MessageQueue是消息队列,Handler将消息投递到MeesageQueue中,而Looper则无限循环地去抽取MessageQueue中的消息并分发给目标Handler,若是没有新消息则堵塞等待。
由于Android 的View不是线程安全的,所以系统只允许在UI线程也就是主线程对UI进行更新操作,所以当试图在子线程更新UI,会抛出异常。因此,我们常在主线程创建Handler实例,然后在子线程持有该实例并调用mHandler.sendMessage()、mHandler.post或是没有手动创建Handler实例,直接mView.post()来在子线程更新UI,而这些方法,都是使用了Handler。当然Handler不只是用来更新UI,我们也可以在子线创建Handler,然后在其他线程对该线程进行控制。
2、Handler的使用
Handler的使用很简单,首先我们要先创建Looper和MessageQueue,有的可能会奇怪,那怎么以前使用Handler来更新UI时怎么没有创建过Looper和MessageQueue呢?原因是主线程的Looper系统已经帮我们创建好了,不需要手动创建了,若是想要在子线程使用Handler,则需要先手动创建Looper,MessageQueue。然后就是创建Handler实例并重写public void handleMessage(Message msg)方法,有一点注意的是:想要通知哪个线程处理事务,handler的实例就要在哪个线程创建,然后由其他线程持有该Handler实例,并通过mHandler.sendMessage()来向目标线程发出消息。例如我们最常用的更新UI,因为我们要通知主线程更新UI,所以我们一定要在主线程创建Handler实例,这样Handler实例就与主线程的Looper、MessageQueue绑定,然后在子线程通过这个handler实例向主线程发出消息。
接下来直接上代码,看看Handler具体是怎么使用的:
在主线程更新UI:
public class MainActivity extends AppCompatActivity {
private Button mButton;
private TextView mTextView;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.test_btn);
mTextView = (TextView) findViewById(R.id.test_iv);
//因为要在主线程更新UI,所以在主线程创建Handler实例
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
//取出Bundle中的数据
String info = msg.getData().getString("info");
//获得message.obj
String str = (String)msg.obj;
mTextView.setText(str);
break;
default:
break;
}
super.handleMessage(msg);
}
};
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//创建Message对象,在该对象中保存信息
Message message = Message.obtain();
//一般以message.waht作为标志
message.what = 0;
//可以直接将任意一个对象附在message.obj
message.obj = "123456789";
//可以创建Bundle对象,并Bundle对象中保存信息
//Bundle中只能保存基本数据类型、String和实现序列化的对象
Bundle bundle = new Bundle();
bundle.putString("info","just for test");
message.setData(bundle);
mHandler.sendMessage(message);
//若是没有需要额外传递的信息,可以使用
//mHandler.sendEmptyMessage(int what)
// 会自动创建一个message对象,并将message.what设为我们传入的参数
}
}).start();
}
});
}
}
在子线程创建使用Handler:
public class MainActivity extends AppCompatActivity {
private Button mButton;
private TextView mTextView;
private Handler mHandler;
private static final String TAG = "HandlerTest";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.test_btn);
mTextView = (TextView) findViewById(R.id.test_iv);
new Thread(new Runnable() {
@Override
public void run() {
//创建Looper,在Looper的构造方法中会创建MessageQueue,因此不必手动创建
Looper.prepare();
//如果想在子线程处理一些事务,如创建Handler实例,
//一定要在Looper.loop()之前处理,因为,Looper.loop()调用之后
//会无限循环,所以在Looper.loop()之后的事务将不会得到处理
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
Looper.myLooper().quit();
}
super.handleMessage(msg);
}
};
//启动Looper,当没调用Looper.myLooper().quit();
// 或Looper.myLooper().quitSafely()
//Looper会一直进行循环下去,子线程也就会一直存在
//因此当不需要时,要终止Looper
Looper.loop();
}
}).start();
//点击Button,第一次点击,通过sendMessage()退出子线程的Looper,并返回true
//第二次点击由于Looper已经退出,sendMessage()将返回false
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//停止Looper,当我们调用mHandler.sendEmptyMessage(),
// 若Looper未停止消息循环 返回true,
//若Looper已停止循环,返回false
boolean isSuccessful = mHandler.sendEmptyMessage(0);
Log.i(TAG, "send message return :"+isSuccessful);
}
});
}
}
3、MessageQueue
MessageQueue中文翻译为消息队列,是来存放Handler传递的消息。虽然MessageQueue叫做消息队列,但是内部实现并不是队列,而是用单链表的数据结构来维护消息列表。MessageQueue有两个主要方法:enqueueMessage()和next()。enqueueMessage()作用是将一条消息插入消息队列,主要就是单链表的插入操作。next()则是从消息队列中读取一条信息,并将该信息从消息队列中删除。next()是一个无限循环循环的方法,如果消息队列中没有消息,就会堵塞,一直等待,直到新消息到来,就会返回新消息,并将该消息从消息队列中删除。
4、Looper
前面的demo提到,Looper.prepare()创建Looper,Looper.loop()启动Looper,现在追踪代码看看源码。
先看下Looper.prepare()
private static void prepare(boolean quitAllowed) {
//因为一个线程只能有一个Looper
//如果sThreadLocal.get() != null,则说明当前线程已经有了一个Looper,会抛出异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建并保存Looper在ThreadLocal对象中
sThreadLocal.set(new Looper(quitAllowed));
}
在看看New Looper(quitAllowed)
private Looper(boolean quitAllowed) {
//在Looper的构造方法里创建了MessageQueue实例
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
这样Looper和MessageQueue就创建完成了。
接下来看看Looper.loop()
public static void loop() {
final Looper me = myLooper();//获取当前线程的Looper实例
if (me == null) {
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 (;;) {
// 调用MessageQueue.next()抽取消息队列的一条消息
//由于next()在消息队列没有消息时会堵塞,所以里也会堵塞
Message msg = queue.next();
if (msg == null) {
//当调用looper.quit()或looper.quitSafely(),MessageQueue.mQuitting属性会被标记
//next会返回null,也只有这种情况next()才会返回null,否则要么堵塞,要么返回消息
//当返回null,Looper直接退出
return;
}
// This must be in a local variable, in case a UI event sets the logger
final 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 {
//把消息分发给将该发送该消息的对象,也就是Handler对象
//msg.target就是发送该消息的对象Handler
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();
}
}
Looper.loop()方法中的几个重要部分都注释清楚了,其中有获取线程Looper的myLooper()方法和分配msg的msg.target.dispatchMessage(msg)我们可以再深入看看
先看看myLooper()
public static @Nullable Looper myLooper() {
//在创建Looper的时候,将Looper保存在了sThreadLocal
//这里通过sThreadLocal.get()获得Looper
return sThreadLocal.get();
}
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程存储数据,且只能在指定线程获取数据,在其他线程无法或取数据。更多关于ThreadLocal的可以查看这篇博客,写的很透彻
再看看msg.target.dispatchMessage(msg),因为msg.target就是发送该条消息的mHandler,也就是mHandler.dispatchMessage(),Looper调用Handler的这个方法来处理消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到,Handler处理消息的步骤是这样的:
首先检查msg.callback是否为空,不为空则调用handlerCallback(msg),
那这个msg.callback是什么呢?其实Message.callback就是我们通过mHandler.post(Runnable)发送消息时的Runnable对象,看handlerCallback(msg)的实现就知道了:
若是
msg.callback为空,就判断mCallback是为为空,若不为空,则调用mCallBack.handleMessage(msg)处理消息。那么问题又来了,这个mHandler.mCallBack又是个什么东西?它是Handler的一个内部接口
private static void handleCallback(Message message) {
message.callback.run();
}
public interface Callback {
public boolean handleMessage(Message msg);
}
它的使用时这样的
Handler.Callback mCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
};
Handler mHandler = new Handler(mCallback);
创建后Handler发送消息的用法跟一般创建的Handler用法一样,就不列出代码了
那这个Handler.Callback存在的意义是什么呢?
“Callback interface you can use when instantiating a Handler to avoid
having to implement your own subclass of Handler.“
having to implement your own subclass of Handler.“
这是源码上的注释,意思是可以使用这个接口来创建Handler实例而不用去派生Handler的实例(但是我实在不明白除了消息处理的优先级比较高,跟我们派生Handler实例有什么区别或者说优点?不都要重写个handleMessage(),代码也没少多少,实在搞不懂为什么要弄这么个接口出来?)
若是mCallback为空,则调用handleMessage(msg)处理消息。
以上就是mHandler.dispatchMessage(msg)的全部过程。
5、Handler
关于Handler,主要是发送消息和处理消息,处理消息的方法dispatchMessage(msg),在刚刚介绍Loop的时候一起分析了,接下来分析下Handler如何发送消息。
在概述部分,提到到过可以通过mHandler.sendMessage(Message)、mHandler.post(Runnable)向目标线程发送消息,甚至mView.post(Runnable)也是使用了Handler来向主线程发送消息更新UI,接下来一个一个分析。
1、sendMessage(Message msg)
首先看看mHandler.sendMessage(),
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
调用了sendMessageDelay(),延迟指定时间再将Message插入MessageQueue,继续往下追踪
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
sendMessageDelay()又调用sendMessageAtTime(),继续追踪
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;
}
//调用了Handler的enqueueMessage(),插入消息
//显然enqueueMessage()内肯定是通过Handler实例保存的MessageQueue实例
//向MessageQueue插入消息
return enqueueMessage(queue, msg, uptimeMillis);
}
我们发现sendMessage()最终调用了sendMessageAtTime(),接着sendMessageAtTime()内又调用了Handler实例的
enqueueMessage(),显然最终将消息插入目标线程MessageQueue的一步就在这个方法里了,我们进入这个方法内部看看
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将发送该消息的Hnndler实例的引用保存在msg.target中
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用保存在Handler实例中目标线程的MessageQueue实例的enqueueMessage()
//将消息插入目标线程的消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
我们看到 msg.target = this,对照前面分析Looper如何分配消息
msg.target.dispatchMessage(msg);
一切就很清楚了,在将消息插入消息队列前,把发送该消息的Handler的实例保存在消息中,这样Looper在主线程非配处理消息时就能准确的把消息交给正确的Handler实例来处理。
2、mHandler.sendEmptyMessage(int what)
我们有时因为只想通知线程,并不需要传递其他数据,就会使用mHanlder.sendEmptyMessage(int what),来向目标线程发送消息,这样的话就省去创建Message实例的步骤了,那mHanlder.sendEmptyMessage(int what)有时如何工作的呢?我们可以追踪这个方法看看
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
我们发现,最终在sendEmptyMessageDelay()里,创建了Message实例,并且what属性就是我们传入的参数,接下来有调用了sendMEssageDelay(),显然,从这里开始,后面的过程与sendMessage()一样。
3、mHandler.post(Runnable r)
我们再看看mHandler.post(Runnable)方法
public final boolean post(Runnable r)
{
//调用了sendMessageDelayed(),该方法的第一个参数应该是一个Message,
//显然getPostMessage(r)创建了一个Message实例
return sendMessageDelayed(getPostMessage(r), 0);
}
显然接下来的过程也跟sendMessage()一样,再看看getPostMessage(r)
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
果然,在这个方法里创建了Message实例,并且把Runnable实例保存在Message的callback中,显然,dispatchMessage()中的msg.callback就是这时候设置的
if (msg.callback != null) {
handleCallback(msg);
}
4、mView.post(Runnable r)
有时候我们要在子线程向更新UI,但又不是很频繁,使用Handler显然有些麻烦,这时候,
使用mView.post(Runnable r)来更新UI是个很好地选择,
我们看看这个方法的工作原理是什么?
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action); //获得了View内部的Handler实例并调用该实例的post()方法
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
我们可以发现mView.post(Runnable r)最终还是通过Handler实例的post(Runnable r)方法。
通过对前面四个向主线程发送消息更新UI方法的分析,我们发现这四种方法都使用了Handler,无论我们是否显式地使用Handler。
总结
经过前面的分析,整个Android线程通信机制应该已经很清晰了:
Handler和本线程的Looper和MessageQueue绑定,一个线程可以有很多个Handler,但只能有一个Looper和MessageQueue;
其他线程通过持有这个Handler实例的引用,可以向这个Handler实例所在的线程发出消息,发送消息是在其他线程进行的;
消息送到消息队列MessageQueue,Looper则不停地抽取消息队列中的消息,Looer循环处理消息是在Handler所在线程也就是目标线程进行的,并把消息交给发送它的Handler实例的handleMessage()方法处理,这样就完成了从其他线程到目标线程的切换,这就是Android线程通信的实质。