Android开发handler源码再次解读记录

源代码解读

  1. 开启线程HandlerThread首先是run方法跑起来,run方法里主要做两件事情,一个是创建Looper,一个是循环Looper
public class HandlerThread extends Thread {
    @Override
    public void run() {
      Looper.prepare();  //创建Looper
      mLooper = Looper.myLooper();  // 然后获取Looper,也就是线程持有Looper。业务代码创建handler的时候调用getLooper()就是得到的这个Looper
      Looper.loop();  //启动循环Looper
    }
}
  1. 看看Looper.prepare()是怎么样给当前线程创建唯一的Looper对象的?
public final class Looper {
  // Looper类有个static的全局变量sThreadLocal,也就是说所有Looper对象共享这个全局变量sThreadLocal。这个变量用法相当于一个Map,它的key是当前线程Thread,键值是Looper。
  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  private static void prepare(boolean quitAllowed) {
      // 判断当前线程是否已经有Looper了?如果不为空就有有了,那就直接报错,就是这里确保了一个线程只能有一个Looper。
      // 面试的时候人家问你一个线程最多有几个Looper知道怎么回答了吧?(回答:一个线程对应一个Looper,通过Looper类的全局变量sThreadLocal确保实现的)
      if (sThreadLocal.get() != null) {
          throw new RuntimeException("Only one Looper may be created per thread");
      }
      // 创建new Looper()放在全局变量存着
      sThreadLocal.set(new Looper(quitAllowed));
  }

  // 获取Looper对象自然是从这个全局变量直接拿了
  public static @Nullable Looper myLooper() {
      return sThreadLocal.get();
  }

  //Looper构造函数直接创建了一个消息队列MessageQueue。那就是一个Looper对应一个消息队列了,面试的时候懂了吧?
  //那一个线程对应多少个消息队列呢?回答:肯定是一个啊,上面不是说了一个线程只有一个Looper了吗?那一个Looper就一个消息列表,所以一个线程就只有一个消息队列!
  private Looper(boolean quitAllowed) {
      mQueue = new MessageQueue(quitAllowed);  // Looper支持MessageQueue消息对应哦,上面备注说了线程持有Looper,那就是线程持间接有MessageQueue了(thread.mLooper.mQueue嘛,这个对后面handler消息派发流程的理解有用哦)
      mThread = Thread.currentThread();
  }
}
  1. 看看Looper.loop()干了啥,死循环为什么主线程卡住?
public final class Looper {
  //for (;;) {} 拿到队列后进去for死循环
  public static void loop() {
    final Looper me = myLooper(); //获取消息队列
    for (;;) {  //死循环消息队列,看到了吧?为什么主线程不会卡住呢?继续往下看,使用了linux的epoll-wait机制实现
      if (!loopOnce(me, ident, thresholdOverride)) {
          return;
      }
    }
  }
  //
  private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) {
      Message msg = me.mQueue.next(); // might block,队列的next方法里使用了linux的epoll-wait机制实现,如果没有消息的时候会进去等待状态,直到超时或者调用了nativeWake(mPtr)方法
      msg.target.dispatchMessage(msg);  //这里的target就是hanlder,看后面的分析就知道
  }
}
public final class MessageQueue {
   Message next() {
      int nextPollTimeoutMillis = 0;//一开始为0,nativePollOnce第一次调用后立即返回
      for (;;) {
          //底层使用epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);实现
          nativePollOnce(ptr, nextPollTimeoutMillis);
          final long now = SystemClock.uptimeMillis();
          Message msg = mMessages; //mMessages是消息队列头指针
          if (msg != null && msg.target == null) { //如果target为空说明是barrier屏障消息
              do {
                  prevMsg = msg;
                  msg = msg.next;
              } while (msg != null && !msg.isAsynchronous());  //if条件target为空遇到了屏障消息后,找到最新异步消息。
              //这里可能有个面试题:怎么保证UI刷新消息优先执行?回答:通过屏障消息+异步消息实现,确保异步消息优先执行。
          }
          if (now < msg.when) {
              // Next message is not ready.  Set a timeout to wake up when it is ready.
              nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);  //next消息时间还没到的话,立即下一次poll_wait等待时间
          }else{
              msg.markInUse();
              return msg;
          }
      }  
  }
}
  1. 一个线程(一个Looper)对应几个Handler?回答:这里就不是只能对应一个了哦,可以多个,你创建了几个就对应几个啊
//创建了几个就对应几个啊,但是你发现没有,每次创建Handler都需要自己Looper的。这个Looper.getMainLooper()可是同一个Looper哦。也就是多个Handler可以共用一个Looper(也共用同一个HandlerThread,共用同一个MessageQueue)。
Handler handler1 = new Handler(Looper.getMainLooper());
Handler handler2 = new Handler(Looper.getMainLooper());
Handler handler3 = new Handler(Looper.getMainLooper());
//Handler内部持有mLooper和mQueue哦
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async, boolean shared) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
    mIsShared = shared;
}
//handler.post一个消息,最终都会调用
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {  //指定时间为负数也没用,会帮你修复为0
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); //SystemClock.uptimeMillis()是手机开机时间哦,不是系统时间。所以为什么修改了系统时间还能确定消息的顺序
}

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
    msg.target = this;  //看到了吧?target就是this哦,这个this就是handler对象哦。注意:同步消息和异步消息都有target,但是屏障消息是没有target的。
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);  //最后调用了队列的enqueueMessage方法将消息添加到队列,等待Looper的消费了。所以为什么Handler构造函数里通过Looper获取了mQueue知道了吧。
}

handler流程图

总结概括

  1. 一个HandlerThread线程只有一个Looper,一个Looper只有一个MessageQueue。Looper通过static类型的sThreadLocal确保每个HandlerThread线程仅有一个Looper。
  2. Handler是可以有多个的,不是只能有一个哦,但是创建Handler的时候需要Looper作为参数,传递的是哪个Looper,最后消息就是进去哪个Looper对应的消息队列。
  3. 通过屏障消息+异步消息实现UI消息(UI消息是异步消息)优先执行,从而避免了消息过多的时候影响到UI的刷新。
posted @ 2025-03-10 16:18  yongfengnice  阅读(64)  评论(0)    收藏  举报