Android消息处理有三大核心类:Looper, Handler和Message,下面就是详细的诠释这三者作用与关联。
Looper
Looper时常被称为消息泵,它是被设计用来使一个普通线程编程Looper线程,所谓的Looper线程就是循环工作的线程。这样的线程会不断循环,一旦有新任务则执行,执行完之后继续等待下一个任务。使用Looper类创建Looper线程只有两行核心代码:
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler
// 开始循环处理消息队列
Looper.loop();
}
}
这样就把该LooperThread线程转化为Looper线程了。
1. Looper.prepare()
图1 普通线程转化为Looper线程
通过Looper类将一个普通线程转化为Looper线程,则在该Looper线程中,有一个Looper对象,它的内部维护了一个消息队列MessageQueue。其中,一个线程中至多只能有一个Looper对象,这一点在源码中可以看出:
public class Looper {
// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper内的消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
// 。。。其他属性
// 每个Looper对象中有它的消息队列,和它所属的线程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
// 我们调用该方法会在调用线程的TLS中创建Looper对象
public static final void prepare() {
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
// 其他方法
}
关于Looper的构造函数:
(1)创建并维护一个MessageQueue 的消息队列;
(2)将Looper对象指向它的实例所在线程(即Looper在创建的时候会关联一个线程);
关于Looper.prepare()函数:
**(1)**ThreadLocal对象用于保存Looper对象的引用,其本身是一个
public static final void loop() {
Looper me = myLooper(); //得到当前线程Looper
MessageQueue queue = me.mQueue; //得到当前looper的MQ
// 这两行没看懂= = 不过不影响理解
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 开始循环
while (true) {
Message msg = queue.next(); // 取出message
if (msg != null) {
if (msg.target == null) {
// message没有target为结束信号,退出循环
return;
}
// 日志。。。
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
// 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
msg.target.dispatchMessage(msg);
// 还是日志。。。
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
// 下面没看懂,同样不影响理解
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf("Looper", "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);
}
// 回收message资源
msg.recycle();
}
}
}
当然,除了prepare()和loop()函数,Looper类还提供了一些有用的方法,比如Looper.myLooper()得到当前线程的Looper对象:
public static final Looper myLooper() {
// 在任意线程调用Looper.myLooper()返回的都是那个线程的looper
return (Looper)sThreadLocal.get();
}
这边可以看到,就是通过ThreadLocal变量拿到Looper对象的。还有getThread()得到Looper对象所在的线程,quit()方法来结束Looper线程。
对于Looper有以下总结:
1).每个线程最多只能有一个Looper对象,并且此对象会存在ThreadLocal中;
2).每个Looper对象中会维护一个MessageQueue的消息队列,loop()方法调用后线程会不断地从队列中取出消息并交由handler去处理;
3).Looper类可以将一个普通线程转化为Looper线程。
Handler
下面问题是如何往MessageQueue上发送消息呢?这就要用到Handler!
Handler是什么呢?handler的主要作用是往MessageQueue上添加和处理消息,当然只会处理由自己发送的消息。Handler在创建的时候会关联一个looper,默认的构造方法将关联当前线程的looper,这个在源码中很清晰:
public class handler {
final MessageQueue mQueue; // 关联的MQ
final Looper mLooper; // 关联的looper
final Callback mCallback;
// 其他属性
public Handler() {
// 没看懂,直接略过,,,
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 = null;
}
// 其他方法
}
下面如果要在LooperThread中加入Handler:
public class LooperThread extends Thread {
private Handler handler1;
private Handler handler2;
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// 实例化两个handler
handler1 = new Handler();
handler2 = new Handler();
// 开始循环处理消息队列
Looper.loop();
}
}
加入Handler实例的效果图如下所示:
图3 加入handler对象的Looper线程
因此可以看得出来:一个LooperThread只能有一个Looper对象,但是可以用多个Handler。
Handler发送消息
有了handler就可以实用post(Runnable),postAtTime(Runnable,long),sendEmptyMessage(int),sendMessage(Message)等,看到这些API可以看出,发送两种类型的消息:Message 和 Runnable,但是其实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;
}
通过handler发送Message有如下特点:
1.message.target即为该handler对象,这样可以确保looper执行到该Message时能够找到处理它的handler,即在loop()函数中的关键代码:
msg.target.dispatchMessage(msg);
2.post发出的消息,其callback为runnable对象;
Handler如何处理消息
消息的处理是通过核心方法dispatchMessage(Message msg)和钩子函数handleMessage(Message msg)完成的,源码如下:
// 处理消息,该方法由looper调用
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果message设置了callback,即runnable消息,处理callback!
handleCallback(msg);
} else {
// 如果handler本身设置了callback,则执行callback
if (mCallback != null) {
/* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */
if (mCallback.handleMessage(msg)) {
return;
}
}
// 如果message没有callback,则调用handler的钩子方法handleMessage
handleMessage(msg);
}
}
// 处理runnable消息
private final void handleCallback(Message message) {
message.callback.run(); //直接调用run方法!
}
// 由子类实现的钩子方法
public void handleMessage(Message msg) {
}
看到这里,开发者可以实现handleMessage函数和Runnable对象的run()函数,以此来完成整个Handler机制!
Handler用处的总结:
1. handler可以在任意线程发送消息,并且这些消息会被添加到关联的MessageQueue上。
图4 handler发送消息到MessageQueue
2. handler是在它关联的Looper线程中来处理消息的。
图5 handler处理Message
这样就解决了Android最经典的不能在其他非主线程中更新UI的问题:Android的主线程必是一个Looper线程,我们在其中创建的handler对象将默认关联主线程的Looper对象(该Looper对象是由系统自动创建的),也就自然关联到该Looper对象锁维护的MessageQueue。其中一个最常见的实用案例就是,创建handler并将其引用传给某个thread,当该thread完成耗时操作后,将实用handler将消息回发到消息队列,再由loop()函数将消息弹出,交由handler的handleMessage(Message msg)的用户自实现的钩子函数来更新UI。
具体实现如图6所示:
给一个实例代码:
public class TestDriverActivity extends Activity {
private TextView textview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textview = (TextView) findViewById(R.id.textview);
// 创建并启动工作线程
Thread workerThread = new Thread(new SampleTask(new MyHandler()));
workerThread.start();
}
public void appendText(String msg) {
textview.setText(textview.getText() + "\n" + msg);
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String result = msg.getData().getString("message");
// 更新UI
appendText(result);
}
}
}
public class SampleTask implements Runnable {
private static final String TAG = SampleTask.class.getSimpleName();
Handler handler;
public SampleTask(Handler handler) {
super();
this.handler = handler;
}
@Override
public void run() {
try { // 模拟执行某项任务,下载等
Thread.sleep(5000);
// 任务完成后通知activity更新UI
Message msg = prepareMessage("task completed!");
// message将被添加到主线程的MQ中
handler.sendMessage(msg);
} catch (InterruptedException e) {
Log.d(TAG, "interrupted!");
}
}
private Message prepareMessage(String str) {
Message result = handler.obtainMessage();
Bundle data = new Bundle();
data.putString("message", str);
result.setData(data);
return result;
}![这里写图片描述](http://img.blog.csdn.net/20150720232945900)
}