Android机制之Handler解析

本文深入解析Android中的消息机制,包括Handler、Looper与MessageQueue的工作原理。详细介绍了如何通过Handler发送消息,Looper如何创建消息循环,以及消息队列如何与线程关联。并通过源代码分析,解释了消息的传递和处理过程。

Android中的消息机制

处理消息的手段—Handler,Looper与MessageQueue:

Android应用启动时,存在一个默认主线程(UI线程),该线程会关联一个消息队列,所有操作被封装成消息交给主流策划功能来处理。保证不退出,将消息操作置入一个死循环中,程序就一直运行,因此不会退出。
这里写图片描述

UI线程的消息循环实在ActivityThread.main方法中创建的,函数源代码如下

public static void main(String[] args){
  //代码省略
  Process.setArgV0("<pre-initialized>);
  Looper.perpareMainLooper();  //创建消息循环Looper

  ActivityThread thread =new ActivityThread();
  thread.attach(false);

  if(sMainThreadHandler == null){
    sMainThreadHandler=thread.getHandler(); //此处获取UI线程的Handler
  }
  AsyncTask.init();
  //代码省略
  Looper.loop(); //2.执行消息循环
  throw new RuntimeException("Main thread loop uexpectedly exited");
}

在子线程中执行耗时操作后需要更新UI,则此时的手段就是通过Handler将消息Post到UI线程中(mHandler.sendEmptyMessage(msg)),然后在Handler中的handleMessage方法进行处理。
实例代码如下:
Post操作:

MyHandler mHandler = new MyHandler();
//开启新线程
new Thread(){
  public void run(){
    //耗时操作
    mHandler.sendEmptyMessage(msg);
  }
}

UI线程处理:(该Handler必须在主线程中创建才可更新UI)

class MyHandler extends Handler{
  @Override
  public void handleMessage(Message msg){
    //更新UI
  }
}

Notes:
- 每个Handler都关联一个消息队列消息队列封装在Looper中,每个Looper又需要关联一个线程(Looper通过ThreadLocal封装),即等于每个消息队列又关联一个线程
- 默认情况下,消息对象只有一个,即主线程的消息队列,该消息队列在ActivityThread.main中创建。通过Looper.perpareMainLooper()创建,最后执行Looper.loop()启动消息队列循环

Handler关联消息队列以及线程源代码解析:

handler.java

//无参数构造方法
public Handler() {
        this(null, false);
}
//含Callback与async的参数构造方法
public Handler(Callback callback, boolean async) {
  mLooper = Looper.myLooper();  //获取Looper
  if (mLooper == null) {
      throw new RuntimeException(
          "Can't create handler inside thread that has not called Looper.prepare()");
  }
  mQueue = mLooper.mQueue;  //获取消息队列
  mCallback = callback;
  mAsynchronous = async;
}

Handler通过Looper.myLooper()获取Looper对象,建立关联,接着对ActivityThread.main方法中调用的Looper.perpareMainLooper()方法进行分析:

Looper.java

//创建ThreadLocal对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

//设置UI线程Looper
public static void prepareMainLooper() {
  prepare(false);  //为当前线程准备一个Looper
  synchronized (Looper.class) {
      if (sMainLooper != null) {
          throw new IllegalStateException("The main Looper has already been prepared.");
      }
      sMainLooper = myLooper();  //为主线程设置Looper
  }
}

//为当前线程设置Looper
private static void prepare(boolean quitAllowed) {
  if (sThreadLocal.get() != null) {
      throw new RuntimeException("Only one Looper may be created per thread");
  }
  sThreadLocal.set(new Looper(quitAllowed));
}

//获取当前线程Looper
public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}

对上述Looper.java源码进行分析,即可得到,系统执行ActivityThread.main方法创建主线程消息循环。main方法中执行perpareMainLooper()方法设置UI线程的Looper,此时Looper对象不存在,执行prepare()方法,通过sThreadLocal.set(new Looper(quitAllowed))方法将新创建的Looper对象与当前线程sThreadLocal关联。此时即完成了消息队列与线程的关联。但是此时存在一个问题,我们无法了解sThreadLocal是否为我们所需要绑定的线程值。接下来对ThreadLocal.java源代码进行分析:

ThreadLocal.java

public class ThreadLocal<T> {
  //省略其他代码
  public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
    //省略其他代码
}

对上述源代码进行分析,T为泛型,从get方法中的第一个行即可看出此方法为获取当前线程值,对结果进行返回。由此可知在Looper.java中对sThreadLocal进行声明与创建,在执行sThreadLocal = new ThreadLocal()方法后,sThreadLocal值已经获取了当前线程值。此时即可说明消息队列与线程的成功关联。而从当前机制即可说明,不同的线程有不同的消息队列,不能随意访问。

此时继续对Handler分析,消息队列通过Looper与线程关联上,而Handler又与Looper关联,因此,Handler最终就和线程,线程的消息队列关联成功。只有更新UI的Handler在主线程中创建,handleMessage在会在UI线程中执行。

创建Looper成功后,需要执行消息循环,而消息循环的建立通过Looper.loop()方法,源代码核心部分如下:

public static void loop() {
  final Looper me = myLooper();
  if (me == null) {
      throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  }
  final MessageQueue queue = me.mQueue;  // 1.获取消息队列
  for (;;) {  // 2.死循环
       Message msg = queue.next();  // 3.依次获取消息
     if (msg == null) {
       // No message indicates that the message queue is quitting.
       return;
    }
    //代码省略
    msg.target.dispatchMessage(msg);  // 4.处理消息
    //代码省略
    msg.recycleUnchecked();  // 5.回收消息
  }
}

从上述程序中可以得出,loop方法中实质就是建立一个死循环,通过消息队列一次取出消息,最后处理消息的过程。对Looper进行总结,通过Looper.perpare()创建Looper对象,且保存在sThreadLocal中,通过Looper.loop()来执行消息循环。这两步通常成对出现,缺一不可。

最后分析消息处理机制,从上述代码中第4步通过msg.target.dispatchMessage(msg)来处理消息。其中msg为Message类型,源代码如下:

public final class Message implements Parcelable{
  Handler target;  // target处理
  Runnable callback;  // Runnable类型的callback
  Message next;  // 下一条消息,消息队列是链式存储

  //代码省略
}

从源代码中可以看出,target是Handler类型。实际上仍旧是通过Handler将消息投递给消息队列,消息队列又将消息分发给Handler来处理。接下来继续从源代码进行分析:

//消息处理函数,子类覆写
public void handleMessage(Message msg){
}

private final void handleCallback(Message msg){
  msg.callback.run();
}

//消息分发
private void dispatchMessage(Message msg){
  if (msg.callback != null){
    handleCallback(msg);
  } else {
    if (mCallback != null){
      if (mCallback.handleMessage(msg)){
        return;
      }
    }
    handleMessage(msg);
  }
}

从上述程序中可以看到,dispatchMessage只是一个分发的方法,如果Runnable类型的callback为空,则执行handleMessage处理消息,该方法为空,则将更新UI的代码写在该函数中;如果callback不为空,则执行handleCallback来处理,该方法调用callback的run方法。则其实是Handler分发的两种类型,比如我们post(Runnable callback)则 callback不为空,我们使用Handler使用sendMessage时通常不会社会资callback,因此,就执行handleMessage这个分支。实现如下:

public final boolean post(Runnable r){
  return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
  Message m = Message.obtain();
  m.callback = r;
  return m;
}

public final boolean sendMessageDelayed(Message msg, long delayMillis){
  if (delayMillis < 0) {
    delayMillis = 0;
  }
  return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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;
  }
  return enqueueMessage(queue, msg, uptimeMillis);
}

从上述代码可以看出,在post(Runnable r)时,会将Runnable包装成Message对象,并且将Runnable对象设置给Message对象的callback字段,最后将该Message对象插入消息队列。

无论是post一个Runnable还是Message,都会调用sendMessageDelayed(msg,time)方法。Handler最终将消息追加到MessageQueue中,而Looper不断的从MessageQueue中读取消息,并调用Handler的dispatchMessage消息,此时消息会源源不断的产生,添加到MessageQueue中。Android应用就成功运转了

Notes:创建Handler之前先执行Looper.perpare(),然后添加Looper.loop().否则将会出现错误.
正确演示:

new Thread(){
  Handler handler =null;
  public void run(){
    Looper.prepare();
    handler=new Handler();
    Looper.loop();
  }
}.start();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值