[转]Android:Looper和Handler分析
靠消息驱动工作的,原理大致都是:
1、有一个消息队列,可以往这个消息队列中投递消息;
2、有一个消息循环,不断从消息队列中取出消息,然后处理。
具体实现过程:
1、事件源把待处理的消息加入到消息队列中,一般是加至队列尾部;一些优先级高的消息也可以加到队列头。
事件源提交的消息可以是按键、触摸屏等物理事件产生的消息,也可以是系统或程序本身发出的请求消息。
2、处理线程不断从消息队列头中取出消息并处理,事件源可以把优先级高的消息放到队列头,这样这些优先级高的消息就会优先被处理。
在Android系统,这些工作主要由Looper和Handler来实现。
Looper类:用于封装消息循环,并且有一个消息队列。
Handler类:有点像辅助类,封装了消息投递、消息处理等接口。
一、Looper类分析
简单的例子:
//定义一个线程类
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
//调用prepare
Looper.prepare();
......
//消息进入循环
Looper.loop();
}
}
//应用程序使用LooperThread
{
......
//启动新线程
new LooperThread().start();
}
1、调用Looper的prepare()方法
public static final void prepare() {
//一个Looper只能调用一次prepare
if(sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created pre thread");
}
//构造一个Looper对象,设置到调用线程的局部变量中
sThreadLocal.set(new Looper());
}
//sThreadLocal定义
private static final ThreadLocal sThreadLocal = new ThreadLocal();
ThreadLocal是Java中的线程局部变量类,全名应该是Thread Local Variable。该类有两个关键函数:
1、set:设置调用线程的局部变量;
2、get:获取调用线程的局部变量。
根据上面的分析可知:prepare()会在调用线程的局部变量中设置一个Looper对象,这个调用线程就是LooperThread的run线程。
Looper对象的构造:
private Looper() {
//构造一个消息队列
mQueue = new MessageQueue();
mRun = true;
//得到当前线程的Thread对象
mThread = Thread.currentThread();
}
总结
prepare()很简单,主要做的事情:在调用prepare()的线程中,设置了一个Looper对象,这个Looper对象就保存在这个调用线程的TLV中,而Looper对象内部封装了一个消息队列。也就是说,prepare()通过ThreadLocal机制,巧妙地把Looper和调用线程管理在一起。
2、Looper循环
看看代码:
public static final void loop() {
//myLooper返回保存在调用线程TLV中的Looper对象
Looper me = myLooper();
//取出这个Looper的消息队列
MessageQueue queue = me.mQueue;
while(true) {
Message msg = queue.next();
//处理消息,Message对象中有一个target,它是Handler类型
//如果target为空,则表示需要退出消息循环
if(msg != null) {
if(msg.target == null) {
return;
}
//调用该消息的Handler,交给它的dispatchMessage方法处理
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
//myLooper方法返回调用线程的线程局部变量,也就是存储在其中的Looper对象
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
通过上面分析发现,Looper的作用是:
1)、封装了一个消息队列;
2)、Looper的prepare()把整个Looper和调用prepare的线程(也就是最终的处理线程)绑定在一起了;
3)、处理线程调用loop(),处理来自该消息队列的消息。
当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列。那么,该消息就将由和Looper绑定的处理线程来处理。
3、Looper、Message和Handler的关系
1)、Looper中有一个Message队列,里面存储的是一个个待处理的Message;
2)、Message中有一个Handler,这个Handler是用来处理Message的。
二、Handler分析
1、初识Handler
Handler所包括的成员:
final MessageQueue mQueue; //Handler中也有一个消息队列
final Looper mLooper; //也有一个Looper
final Callback mCallback; //也有一个回调用的类
Handler有4个构造函数,它们的主要区别就是对上面的三个成员变量的初始化上。
//构造函数1
public Handler() {
//获得调用线程的Looper
mLooper = Looper.mLooper();
if(mLooper == null){
throw new RuntimeException(......);
}
//得到Looper的消息队列
mQueue = mLooper.mQueue;
//无callback设置
mCallback = null;
}
//构造函数2
public Handler(Callback callback) {
//获得调用线程的Looper
mLooper = Looper.mLooper();
if(mLooper == null){
throw new RuntimeException(......);
}
//得到Looper的消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
}
//构造函数3
public Handler(Looper looper) {
//由外部传入
mLooper = looper;
//得到Looper的消息队列
mQueue = mLooper.mQueue;
mCallback = null;
}
//构造函数4
public Handler(Looper looper,Callback callback) {
//由外部传入
mLooper = looper;
//得到Looper的消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
}
在上述的这些构造方法中,Handler的消息队列变量最终都会指向Looper的消息队列。
2、Handler的真面目
根据前面的分析可知:Handler的消息队列其实就是某个Looper的消息队列。
有个问题:怎样往Looper的消息队列插入消息?
如果没有使用Handler,可以使用这样原始的办法:
1)、调用Looper的mQueue,它将返回消息队列对象的MessageQueue;
2)、构造一个Message,填充它的成员,尤其是target变量;
3)、调用MessageQueue的enqueueMessage,将消息插入到消息队列。
这种原始办法很麻烦,且容易出错。现在有了Handler,就变得简单多了。
2.1、Handler和Message
Handler提供了一系列方法,帮助我们完成创建消息和插入消息队列的工作。例如:
//查看消息队列是否有消息码是what的消息
final boolean hasMessage(int what)
//从Handler创建一个消息码是what的消息
final Message obtainMessage(int what)
//从消息队列中移除消息码是what的消息
final void removeMessage(int what)
//发送一个只填充了消息码的消息
final boolean sendEmptyMessage(int what)
//发送一个消息,该消息被添加到队列尾
final boolean sendMessage(Message msg)
//发送一个消息,给消息被添加到队列头,所以优先级较高
final boolean sendMessageAtFrontOfQueue(Message meg)
现在以sendMessage为例:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg,0); //调用sendMessageDelayed()
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if(delayMillis < 0) {
delayMillis = 0;
}
//调用sendMessageAtTime,把当前时间算上
return sendMessageAtTime(msg, SystemClock.uptimeMillis + delayMillis);
}
可以知道,delayMillis是以当前时间为基础的相对时间。
public final boolean sendMessageAtTime(Message msg, long uptimeMillis) {
boolean sent = false;
MessageQueue queue = mQueue;
if(queue != null){
//把Message的target设置为自己,然后加入到消息队列中
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
return sent;
}
Handler把Message的target设置为自己,是因为Handler除了封装消息添加等功能外,还封装了消息处理的接口。
2.2、Handler的消息处理
刚才我们往Looper的消息队列添加了一条消息,按照Looper的处理规则,它在获取消息后会调用target的dispatchMessage方法,再把这个消息派发给Handler处理。
那么Handler是如何处理的呢?
public void dispatchMessage(Message msg){
//如果Message本身有callback,则直接交给Message的callback处理
if(msg.callback != null){
handlerCallback(msg);
} else {
//如果本Handler设置了mCallback,则交给mCallback处理
if(mCallback != null){
if(mCallback.handleMessage(msg)) {
return;
}
}
//最后才是交给子类处理
handleMessage(msg);
}
}
dispatchMessage定义了一套消息处理的优先级机制,它们分别是:
1)、Message如果自带了callback处理,则交给callback处理;
2)、Handler如果设置了全局的mCallback,则交给mCallback处理;
3)、如果上述都没有,该消息会被交给Handler子类实现的handleMessage处理。当然,这须要从Handler派生并重载handleMessage方法。
在通常情况下,我们都采用第三种,即在子类中通过重载handleMessage方法来完成处理工作。
三、Looper和Handler的同步关系
它们之间确实有同步关系,如果不注意这点,可能会发生错误。
先看一个例子:
//先定义一个LooperThread类
class LooperThread extends Thread {
public Looper myLooper = null; //定义一个myLooper,初值为空
//假设run在线程2执行
public void run () {
Looper.prepare();
//myLooper必须在这个线程中赋值
myLooper = Looper.myLooper();
Looper.loop();
}
}
//下面这段代码在线程1执行,并且会创建线程2
{
LooperThread lpThread =new LooperThread;
lpThread.start(); //start后会创建线程2
Looper looper = lpThread.myLooper; // --注意
//thread2Handler和线程2的Looper挂上钩
Handler thread2Handler = new Handler(looper);
//sendMessage发送的消息将由线程2处理
thread2Handler.sendMessage(...)
}
1)、线程1中创建线程2,并且线程2通过Looper处理消息;
2)、线程1中得到线程2的Looper,并且根据这个Looper创建一个Handler,这样发送给该Handler的消息将由线程2处理。
但是这样的代码是有问题的:
在标有注意的那里,myLooper的创建是在线程2中,而looper的赋值是在线程1中,很有可能此时线程2的run方法还没来得及给myLooper赋值,这样线程1中的looper将去到myLooper的初值null,即looper等于null。
另外:
Handler thread2Handler = new Handler(looper);
//不能替换成下面的
Handler thread2Handler = new Handler(Looper.myLooper());
这是因为,myLooper返回的是调用调用线程的Looper,即线程1的Looper,而不是我们要的线程2的Looper。
对于这个问题,可以采用同步的方式来处理。Android提供了一个HandlerThread来解决这个问题。
四、HandlerThread
看看代码:
public class HandlerThread extends Thread {
//线程1调用getLooper来获得新线程的Looper
public Looper getLooper() {
......
synchronized (this) {
while(isAlive && mLooper == null) {
try {
wait(); //如果新线程还未创建Looper,则等待
} catch (...)
}
}
return mLooper;
}
//线程2运行它的run方法,looper就是在run线程里创建的
public void run() {
mTid = Process.myTid;
Looper.prepare(); //创建这个线程上的Looper
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //通知取Looper的线程1,此时Looper已经创建好了
}
Process.setThreadPriority(mPriority);
onLooperPrepare();
Looper.loop();
mTid = -1;
}
}

浙公网安备 33010602011771号