Android Handler源码浅析

Android Handler源码解析

大家都知道Handler是Android系统的一种异步机制,常用在子线程中更新UI线程,听说面试的时候HR经常会问“你分析过Handler的源码吗?”,博主出于这种不纯的动机分析了一下Handler的源码 ^_^(别鄙视我)。

首先,大家一般想要使用一个对象的时候用的最多的关键字是什么呢?对,就是new这个关键字,他创建了一个对象供我们使用,现在我们就从Handler的构造汉初开始分析。

代码块1
 public Handler(Callback callback, boolean async) {
   ........................省略
    mLooper = Looper.myLooper();
    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;
}

首先它通过一个mylooper方法得到了一个Looper然后判断mLooper,如果mLooper不为空就把mLooper所在线程的消息队列赋值给mQueue。那么什么时候mLooper是空的呢?看看myLooper方法的代码。

代码块2
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

从这里看出Looper 保存在ThreadLocal里面,ThreadLocal这个类在这里解释一下,这个类有两个方法get( )set( ),比如说我有一个叫nameThreadLocal的ThreadLocal对象,我在线程A里面调用了nameThreadLocal.set("ThreadA");在线程B里面掉用了nameThreadLocal.set("ThreadB");那么我在线程A get到的就是nameA,在线程B get到的就是nameB。你可以认为每个线程都有一个袋子,在哪个线程set,get就是在哪个线程拿和放,ThreadLocal会自动判定你的线程,从而选用对应的袋子。好了,我们接着分析代码,如果当前线程没有looper那么这个方法会返回空,从Handler的构造函数可以看出mLooper为null时,它会抛出如下异常:

代码块3
 if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");

意思是“不能在没有调用Looper.prepare( )的线程里创建Handler”,(强调一下,我们一般在UI线程里创建Handler实例不会报这个异常是因为系统创建UI线程时会自动调用Looper.prepare( )方法,但是我们自己创建的子线程必须手动调用prepare方法)那我们就看一下Looper的prepare方法:

代码块4
    public static void prepare() {
        prepare(true);
    }

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));
}

上面的prepare调用的是下面的prepare,它会判断当前线程是否有looper,如果没有就创建一个并放到sThreadLocal里面,所以执行完了这个方法之后,Handler的构造函数就不会报异常了。下面我们看看Looper的构造函数:

代码块5
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

在构造函数中它创建了一个消息对列,我们回到代码块1 可以看到mQueue = mLooper.mQueue————–Looper把这个消息队列交给了Handler。这样调用了Lopper.prepare( )并且创建了handler实例时候我们这个线程Handler,Looper,MessageQueue这三个对象都有了,下面我们来看看这三个对象是怎么工作的。


我们可以从handler.sendMessageAtTime (Message msg)这个对象着手分析因为所有发送消息的方法最终都是调用这个方法,这个方法会发送一个Message到消息队列交给Handler处理。

代码块6
    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);
}

这里直接看return这句,enqueueMessage方法会把send过来的方法放入消息队列,继续点进去enqueueMessage方法,最终发现它调用的是下面这个方法:

代码块7
boolean enqueueMessage(Message msg, long when) {
    ...........................略
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

这段没什么就是把这个消息加入消息队列队尾。最后就是看看消息是怎么取出然后怎么送到handler的了。这就要看看Looper的loop方法。

代码块8
public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    .....................................略
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }
        .....................................略
        msg.target.dispatchMessage(msg);
        .....................................略
                    }

        msg.recycleUnchecked();
}

代码比较乱,只截取了比较重要的loop方法首先得到了当前线程的Looper然后得到MessageQueue,然后使用 msg.target.dispatchMessage(msg); 这行代码分发给handler。msg.target就是要发送到的handler,但是我们平时用Message都是直接new出来的,也没有设置target啊,答案就在下面的代码,很简单。

  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

看到了吗,msg.target = this;就是这一行。好了大致的流程就是这样,感觉看着还是很简单的。^_^ 下面我们来根据来自官方文档的代码总结一下。

 class LooperThread extends Thread {
  public Handler mHandler;

  public void run() {
      Looper.prepare();

      mHandler = new Handler() {
          public void handleMessage(Message msg) {
              //处理任务
            }
      };

      Looper.loop();
  }

}

从上面代码来看:
第一步:调用Looper.prepare方法(子线程需要手动调用,UI线程不需要),在当前线程创建了一个looper同时创建了一个MessageQueue并且保存起来。

第二步:创建了一个Handler并且重写了处理消息的方法并得到了当前线程的Looper和MessageQueue。

第三步:执行Looper.loop( ).这句代码会得到当前线程的looper和MessageQueue,并且不断地从MessageQueue取出消息送到Handler去处理。

上面做好了之后 当我们调用mHandler.sendMessageAtTime (new Message());
mHandler把这个消息的target设为当前mHandler然后把它加入到MessageQueue等待looper取出。

好了,当前就了解这么多,是不是感觉对handler了解了很多。由于博主刚刚开始写博客,水平有限,如果有错误请大家指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值