从新了解Android中Handler

简介

知识的获取须要知其一,还要知其二,有机会还要知其三啦。java

在Android中常常会使用到Handler,或者在View的更新中常常能见到Handler的代码,只有主线程才能操做UI这是咱们的固定思惟。咱们一般在子线程作完耗时操做后,通知主线程去更新UI,通常都是经过Handler发送消息,主线程接收消息后执行对应的逻辑。android

那么不妨问问几个问题:markdown

  1. Handler是什么?
  2. 为何要用Handler机制去更新UI呢?
  3. 消息的发送与处理经历了哪些?
  4. 非UI线程真的不能更新UI吗?(第二篇讲)

若是能一下说明白,大佬请慢些走,手动奉杯茶【滑稽】。网络

Handler是什么?

handler是android给咱们提供用来更新UI的一套机制,也是一套消息处理机制(这是重点),咱们能够发送消息,也能够经过它处理消息。在系统的源码中,activity也会经过Handler机制发送消息回调生命周期函数。并发

为何要用Handler机制去更新UI呢

咱们常常会在子线程去处理大量运算、网络请求、图片压缩等操做。当咱们操做完,要通知主线程操做结果,能够进行下一步行动了。这时候又有一个问题,若是几个线程同时去通知主线程呢,那么必然会有并发问题,咱们怎么避免这个问题又不会影响性能呢?(加同步锁会影响性能)函数

Google大哥仍是了解开发者意愿的,搬出来了Handler机制,内部有Looper、MessageQueue两个重要角色,光看名字咱们就知道这是一个消息队列,经过队列方式去对UI进行更新。很好解决了多个子线程的并发问题,还不影响性能。oop

消息的发送与处理经历了哪些?

第一步确定是从new Handler()开始了。Looper、MessageQueue、CallBack,能够看到咱们熟悉的3个对象都有了。post

在经过Looper.myLooper得到looper对象时,若是Looper为空,那么会抛出异常(提示:不能在未调用Looper.prepare()的线程里建立Handler)。在Android的主线程中,启动时便已调用过,能够直接建立Handler,可是在其它子线程里,直接建立Handler是会致使应用崩溃的。性能

avatar

接着从Looper中获取了MessageQueue对象了,存储消息都是保存在这里面。ui

Looper对象

进入myLooper方法,能够看到对象是经过ThreadLocal获取的,ThreadLocal是一个线程局部变量(map结构),和普通变量的不一样在于,每一个线程持有这个变量的一个副本,能够独立调用set和get方法,而且线程之间不会发生冲突(避免并发)。

/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */
public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}

复制代码

既然知道了ThreadLocal是什么,那么就去看看在哪调用它的set方法。

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));
}
复制代码

发现果真是在调用Looper.prepare()建立了一个Looper对象,而后将对象存入到了sThreadLocal线程局部变量中。一样在一个线程中重复调用了prepare()方法,发现sThreadLocal已经不为空了,是会抛出异常的,提示“一个线程中只能有一个Looper对象”。

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

这说明建立的Looper都是独立且惟一的,到这里Looper的建立过程也就完毕了。

消息的接收与发送

handler.sendEmptyMessage()发送一个空消息,实际上几个发送消息方法最终调用的都是sendMessageAtTime()方法,而后执行queue.enqueueMessage()将消息存入队列中(注意看,target默认指向的是Handler本身)。

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

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  msg.target = this;  //target指向的是Handler本身
  if (mAsynchronous) {
    msg.setAsynchronous(true);
  }
  return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

接下来再看看消息的发送,咱们知道发送是调用的Looper.loop()方法,直接进入源码。

看到先获取并判断Looper对象是否为空,而后拿到MessageQueue消息队列。在下面定义了一个死循化,不停的从队列中取值,若是为空则return掉,不为空则执行msg.target.dispatchMessage(msg)。敲黑板了!!!,这里的target以前说过默认状况是Handler,将信息又丢给Handler本身处理。

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;

  for (;;) {
    Message msg = queue.next(); // might block
    if (msg == null) {
      // No message indicates that the message queue is quitting.
      return;
    }
		//....省略一大堆代码
    try {
      msg.target.dispatchMessage(msg);
      dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } finally {
      if (traceTag != 0) {
        Trace.traceEnd(traceTag);
      }
    }
    //...再省略一大堆代码

    msg.recycleUnchecked();
  }
}
复制代码

在这里咱们就介绍完了Message的存储与发送,在Handler本身去处理消息时,会判断callback是否为空(去看看post(runnable)),为空则执行handleMessage(msg)方法,让咱们本身跟本身去完了。

public void dispatchMessage(Message msg) {
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}
复制代码

总结

彩蛋来了,来一篇图解Handler吧。

我(Handler)对十元(Looper)说:“我喜欢你,作我女友吧(发送消息给Looper)”,十元接收到后确定不能立马就答应我啊,女孩子要矜持嘛,要想想:“我要不要接受他,他之后还会不会对我好,我不接受他他会不会很伤心,万一这帅哥跳河了怎么办(Looper.loop)”。而后过了一会想通了,以为嗯能够的,回答我:“好吧(Looper分发消息给Handler)”。这回到我了,我接收到消息后确定不是结束啊,还要想着两人的将来的,如今的年轻人又不是肯定关系就能结婚的,固然是选择继续关爱、呵护她呀(消息处理)。 (未完待续第二篇)