[转]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;
    }
  }

 

posted @ 2013-04-23 23:24  XuShao  阅读(474)  评论(0)    收藏  举报