likes
comments
collection
share

重新了解Android中Handler

作者站长头像
站长
· 阅读数 7

简介

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

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

那么不妨问问几个问题:

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

如果能一下说明白,大佬请慢些走,手动奉杯茶【滑稽】。

Handler是什么?

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

为什么要用Handler机制去更新UI呢

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

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

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

第一步肯定是从new Handler()开始了。Looper、MessageQueue、CallBack,可以看到我们熟悉的3个对象都有了。

在通过Looper.myLooper获得looper对象时,如果Looper为空,那么会抛出异常(提示:不能在未调用Looper.prepare()的线程里创建Handler)。在Android的主线程中,启动时便已调用过,可以直接创建Handler,但是在其它子线程里,直接创建Handler是会导致应用崩溃的。

重新了解Android中Handler

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

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吧。

重新了解Android中Handler

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