Handler/Looper/MessageQueue 深度解析(原理 + 场景 + 面试题)

83 阅读5分钟

Handler/Looper/MessageQueue 深度解析(原理 + 场景 + 面试题)

1. Handler 延时消息原理详解

场景:延时消息与定时任务

image.png

延时消息底层原理

public class MessageQueue {
  private Message mMessages; // 消息链表头
  private boolean mQuit = false;
  
  Message next() {
    for (;;) {
      if (mQuit) return null;
      
      long nextPollTimeoutMillis = 0;
      synchronized (this) {
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;
        
        if (msg != null && msg.target == null) {
          // 遇到同步屏障,查找异步消息
          do {
            prevMsg = msg;
            msg = msg.next;
          } while (msg != null && !msg.isAsynchronous());
        }
        
        if (msg != null) {
          if (now < msg.when) {
            // 消息时间未到,计算等待时间
            nextPollTimeoutMillis = msg.when - now;
          } else {
            // 消息时间到了,返回消息
            if (prevMsg != null) {
              prevMsg.next = msg.next;
            } else {
              mMessages = msg.next;
            }
            msg.next = null;
            return msg;
          }
        }
      }
      
      // 关键:使用 epoll 机制等待
      nativePollOnce(ptr, nextPollTimeoutMillis);
    }
  }
  
  boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
      msg.when = when;
      Message p = mMessages;
      boolean needWake;
      
      if (p == null || when == 0 || when < p.when) {
        // 插入到链表头部
        msg.next = p;
        mMessages = msg;
        needWake = mBlocked;
      } else {
        // 按时间顺序插入到链表中间
        needWake = mBlocked && p.target == null && msg.isAsynchronous();
        Message prev;
        for (;;) {
          prev = p;
          p = p.next;
          if (p == null || when < p.when) {
            break;
          }
          if (needWake && p.isAsynchronous()) {
            needWake = false;
          }
        }
        msg.next = p;
        prev.next = msg;
      }
      
      if (needWake) {
        nativeWake(mPtr);
      }
    }
    return true;
  }
}

为什么不会阻塞主线程?

  1. epoll 机制:nativePollOnce() 使用 Linux epoll 机制,当没有消息时线程会进入等待状态,不占用 CPU

  2. 时间计算:根据下一条消息的时间计算等待时长

  3. 唤醒机制:有新消息时通过 nativeWake() 唤醒线程

面试题:延时消息的精确性

  • 问题:Handler 的延时消息是否精确?
  • 答案:不精确,原因:
  • 消息按时间排序,但处理有延迟
  • 主线程繁忙时,消息处理会延迟
  • 系统休眠时,时间计算可能不准确

2. 消息屏障与异步消息

场景:VSync 信号处理

  private val handler = Handler(Looper.getMainLooper())
  private val asyncHandler = Handler(Looper.getMainLooper()) { msg ->
    Log.d("Barrier", "异步消息处理: ${msg.what}")
    true
  }
  
  fun demonstrateBarrier() {
    Log.d("Barrier", "开始演示消息屏障")
    
    // 1. 插入同步屏障
    val token = handler.postSyncBarrier()
    Log.d("Barrier", "插入同步屏障")
    
    // 2. 普通消息被阻塞
    handler.post {
      Log.d("Barrier", "普通消息1 - 被阻塞")
    }
    
    handler.post {
      Log.d("Barrier", "普通消息2 - 被阻塞")
    }
    
    // 3. 异步消息可以越过屏障
    val asyncMsg1 = Message.obtain(asyncHandler, 1)
    asyncMsg1.isAsynchronous = true
    handler.sendMessage(asyncMsg1)
    
    val asyncMsg2 = Message.obtain(asyncHandler, 2)
    asyncMsg2.isAsynchronous = true
    handler.sendMessage(asyncMsg2)
    
    // 4. 移除屏障
    handler.removeCallbacksAndMessages(token)
    Log.d("Barrier", "移除同步屏障,普通消息开始处理")
  }
}

// VSync 信号处理模拟
class VSyncHandler {
  private val handler = Handler(Looper.getMainLooper())
  private var vsyncToken: Any? = null
  
  fun onVSync() {
    // VSync 信号到达时插入屏障
    vsyncToken = handler.postSyncBarrier()
    
    // 发送异步消息处理帧渲染
    val frameMsg = Message.obtain(handler) {
      Log.d("VSync", "处理帧渲染")
      // 执行 onDraw 等渲染逻辑
    }
    frameMsg.isAsynchronous = true
    handler.sendMessageAtFrontOfQueue(frameMsg)
    
    // 移除屏障
    handler.removeCallbacksAndMessages(vsyncToken)
  }
}

消息屏障原理

Message next() {
  for (;;) {
    synchronized (this) {
      final long now = SystemClock.uptimeMillis();
      Message prevMsg = null;
      Message msg = mMessages;
      
      if (msg != null && msg.target == null) {
        // 遇到同步屏障(target == null)
        do {
          prevMsg = msg;
          msg = msg.next;
        } while (msg != null && !msg.isAsynchronous());
        // 只处理异步消息,跳过普通消息
      }
      
      if (msg != null) {
        if (now < msg.when) {
          // 等待
        } else {
          // 返回消息
          if (prevMsg != null) {
            prevMsg.next = msg.next;
          } else {
            mMessages = msg.next;
          }
          msg.next = null;
          return msg;
        }
      }
    }
  }
}

异步消息使用场景

  1. VSync 信号:确保帧渲染优先处理
  1. 输入事件:触摸、按键事件需要及时响应
  1. 系统消息:系统级的高优先级消息

面试题:为什么异步消息优先级更高?

  • 答案:在同步屏障存在时,只有异步消息能被处理,普通消息被阻塞
  • 应用场景:VSync 信号、输入事件、系统消息
  • 实现原理:通过 msg.isAsynchronous() 标记,在屏障时跳过普通消息

3. 主线程 Looper 创建

场景:主线程初始化

  fun demonstrateMainLooper() {
    Log.d("Looper", "主线程: ${Thread.currentThread().name}")
    Log.d("Looper", "主线程 Looper: ${Looper.myLooper()}")
    Log.d("Looper", "主线程 MessageQueue: ${Looper.myLooper()?.queue}")
  }
}

// ActivityThread.java 中的主线程初始化
class ActivityThread {
  public static void main(String[] args) {
    // 1. 准备主线程 Looper
    Looper.prepareMainLooper();
    
    // 2. 创建 ActivityThread
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    
    // 3. 开始消息循环
    Looper.loop();
  }
}

// Looper.java 简化版
class Looper {
  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  
  public static void prepareMainLooper() {
    prepare(false); // 主线程不允许退出
    synchronized (Looper.class) {
      if (sMainLooper != null) {
        throw new IllegalStateException("主线程 Looper 已创建");
      }
      sMainLooper = myLooper();
    }
  }
  
  private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
      throw new RuntimeException("每个线程只能有一个 Looper");
    }
    sThreadLocal.set(new Looper(quitAllowed));
  }
  
  public static Looper myLooper() {
    return sThreadLocal.get();
  }
  
  public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
      throw new RuntimeException("没有调用 prepare()");
    }
    
    final MessageQueue queue = me.mQueue;
    for (;;) {
      Message msg = queue.next(); // 可能阻塞
      if (msg == null) {
        return;
      }
      
      msg.target.dispatchMessage(msg);
      msg.recycleUnchecked();
    }
  }
}

ThreadLocal 原理

  // 每个线程都有独立的 ThreadLocalMap
  public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
      map.set(this, value);
    } else {
      createMap(t, value);
    }
  }
  
  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();
  }
}

// ThreadLocalMap 数据结构
class ThreadLocalMap {
  static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
      super(k);
      value = v;
    }
  }
  
  private Entry[] table;
  private int size = 0;
  private static final int INITIAL_CAPACITY = 16;
}

面试题:为什么主线程不用创建 Looper?

  • 答案:在 ActivityThread.main() 中已经调用了 Looper.prepareMainLooper()
  • 时机:应用启动时,在 Activity 创建之前
  • 特点:主线程 Looper 不允许退出(quitAllowed = false)

4. IdleHandler 与低优先级任务

场景:空闲时执行任务

  private val handler = Handler(Looper.getMainLooper())
  
  fun demonstrateIdleHandler() {
    Log.d("IdleHandler", "开始演示")
    
    // 添加 IdleHandler
    Looper.myLooper()?.queue?.addIdleHandler(object : MessageQueue.IdleHandler {
      override fun queueIdle(): Boolean {
        Log.d("IdleHandler", "主线程空闲,执行低优先级任务")
        
        // 执行一些不紧急的任务
        performLowPriorityTask()
        
        return false // 移除这个 IdleHandler
      }
    })
    
    // 添加持续性的 IdleHandler
    Looper.myLooper()?.queue?.addIdleHandler(object : MessageQueue.IdleHandler {
      override fun queueIdle(): Boolean {
        Log.d("IdleHandler", "定期检查任务")
        checkPeriodicTask()
        return true // 保留,下次空闲时继续执行
      }
    })
    
    // 模拟一些任务
    repeat(5) { i ->
      handler.post {
        Log.d("IdleHandler", "执行任务 $i")
        Thread.sleep(100) // 模拟耗时
      }
    }
  }
  
  private fun performLowPriorityTask() {
    // 预加载数据
    preloadData()
    // 清理缓存
    cleanCache()
    // 统计信息
    collectStats()
  }
  
  private fun checkPeriodicTask() {
    // 检查网络状态
    checkNetworkStatus()
    // 检查更新
    checkForUpdates()
  }
}

// 启动优化中的 IdleHandler 使用
class StartupOptimizer {
  fun optimizeStartup() {
    // 关键初始化
    initCriticalComponents()
    
    // 非关键初始化延迟到空闲时
    Looper.myLooper()?.queue?.addIdleHandler(object : MessageQueue.IdleHandler {
      override fun queueIdle(): Boolean {
        Log.d("Startup", "主线程空闲,执行非关键初始化")
        
        // 预加载一些数据
        preloadUserData()
        // 初始化一些服务
        initNonCriticalServices()
        // 预热一些组件
        warmupComponents()
        
        return false // 只执行一次
      }
    })
  }
}

IdleHandler 原理

Message next() {
  for (;;) {
    synchronized (this) {
      // ... 消息处理逻辑
      
      if (msg != null) {
        // 有消息,处理消息
        return msg;
      }
    }
    
    // 没有消息时,处理 IdleHandler
    if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
      pendingIdleHandlerCount = mIdleHandlers.size();
    }
    
    if (pendingIdleHandlerCount <= 0) {
      // 没有 IdleHandler,继续等待
      mBlocked = true;
      continue;
    }
    
    if (mPendingIdleHandlers == null) {
      mPendingIdleHandlers = mIdleHandlers.toArray(new IdleHandler[mIdleHandlers.size()]);
    }
    
    // 执行 IdleHandler
    for (int i = 0; i < pendingIdleHandlerCount; i++) {
      final IdleHandler idler = mPendingIdleHandlers[i];
      mPendingIdleHandlers[i] = null;
      
      boolean keep = false;
      try {
        keep = idler.queueIdle();
      } catch (Throwable t) {
        Log.wtf("IdleHandler", "IdleHandler threw exception", t);
      }
      
      if (!keep) {
        synchronized (this) {
          mIdleHandlers.remove(idler);
        }
      }
    }
    
    pendingIdleHandlerCount = 0;
    nextPollTimeoutMillis = 0;
  }
}

面试题:IdleHandler 的使用场景

  • 启动优化:非关键初始化延迟到空闲时
  • 预加载:空闲时预加载数据
  • 清理任务:定期清理缓存、统计信息
  • 监控任务:检查网络状态、更新等

5. IntentService 与后台任务

场景:后台任务处理

  
  override fun onHandleIntent(intent: Intent?) {
    Log.d("IntentService", "开始处理任务: ${intent?.action}")
    
    when (intent?.action) {
      "UPLOAD_FILE" -> {
        val filePath = intent.getStringExtra("file_path")
        uploadFile(filePath)
      }
      "SYNC_DATA" -> {
        syncData()
      }
      "CLEAN_CACHE" -> {
        cleanCache()
      }
    }
    
    Log.d("IntentService", "任务处理完成")
  }
  
  private fun uploadFile(filePath: String?) {
    // 模拟文件上传
    Thread.sleep(5000)
    Log.d("IntentService", "文件上传完成: $filePath")
  }
  
  private fun syncData() {
    // 模拟数据同步
    Thread.sleep(3000)
    Log.d("IntentService", "数据同步完成")
  }
  
  private fun cleanCache() {
    // 模拟清理缓存
    Thread.sleep(2000)
    Log.d("IntentService", "缓存清理完成")
  }
}

// 使用 IntentService
class ServiceExample {
  fun startBackgroundTask() {
    val intent = Intent(context, MyIntentService::class.java).apply {
      action = "UPLOAD_FILE"
      putExtra("file_path", "/path/to/file")
    }
    startService(intent)
  }
}

IntentService 原理

public abstract class IntentService extends Service {
  private volatile Looper mServiceLooper;
  private volatile ServiceHandler mServiceHandler;
  private String mName;
  private boolean mRedelivery;
  
  private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
      super(looper);
    }
    
    @Override
    public void handleMessage(Message msg) {
      onHandleIntent((Intent) msg.obj);
      stopSelf(msg.arg1);
    }
  }
  
  @Override
  public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
    
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }
  
  @Override
  public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
  }
  
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
  }
  
  @Override
  public void onDestroy() {
    mServiceLooper.quit();
  }
  
  protected abstract void onHandleIntent(Intent intent);
}

HandlerThread 原理

public class HandlerThread extends Thread {
  int mPriority;
  int mTid = -1;
  Looper mLooper;
  
  public HandlerThread(String name) {
    super(name);
    mPriority = Process.THREAD_PRIORITY_DEFAULT;
  }
  
  @Override
  public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
      mLooper = Looper.myLooper();
      notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
  }
  
  public Looper getLooper() {
    if (!isAlive()) {
      return null;
    }
    
    synchronized (this) {
      while (isAlive() && mLooper == null) {
        try {
          wait();
        } catch (InterruptedException e) {
        }
      }
    }
    return mLooper;
  }
}

面试题:IntentService 的特点

  • 单线程:使用 HandlerThread 确保单线程处理
  • 自动停止:任务完成后自动调用 stopSelf()
  • 队列处理:按顺序处理 Intent
  • 后台执行:不阻塞主线程

6. Handler 解决的实际问题

场景:线程间通信

  private val mainHandler = Handler(Looper.getMainLooper())
  private val backgroundHandler = Handler(Looper.getMainLooper())
  
  fun demonstrateCommunication() {
    Log.d("Communication", "主线程开始")
    
    // 后台线程执行任务
    Thread {
      Log.d("Communication", "后台线程开始工作")
      
      // 模拟耗时操作
      Thread.sleep(2000)
      
      // 回到主线程更新 UI
      mainHandler.post {
        Log.d("Communication", "回到主线程更新 UI")
        updateUI()
      }
      
      // 发送消息
      val msg = Message.obtain(mainHandler, 1)
      msg.obj = "任务完成"
      mainHandler.sendMessage(msg)
      
    }.start()
  }
  
  private fun updateUI() {
    // 更新 UI 组件
  }
}

// 定时任务
class TimerExample {
  private val handler = Handler(Looper.getMainLooper())
  private val runnable = object : Runnable {
    override fun run() {
      Log.d("Timer", "定时任务执行: ${System.currentTimeMillis()}")
      handler.postDelayed(this, 1000) // 继续下一次
    }
  }
  
  fun startTimer() {
    handler.post(runnable)
  }
  
  fun stopTimer() {
    handler.removeCallbacks(runnable)
  }
}

// 延迟执行
class DelayExample {
  private val handler = Handler(Looper.getMainLooper())
  
  fun delayExecute() {
    Log.d("Delay", "开始延迟执行")
    
    handler.postDelayed({
      Log.d("Delay", "5秒后执行")
    }, 5000)
  }
}

Handler 解决的问题

  1. 线程间通信:后台线程 → 主线程更新 UI
  1. 定时任务:定期执行某些操作
  1. 延迟执行:延迟执行任务
  1. 消息队列:有序处理消息
  1. 异步处理:不阻塞主线程

7. 衍生面试题

1. Handler 内存泄漏

  // 错误示例:匿名内部类持有外部引用
  private val handler = Handler(Looper.getMainLooper())
  
  fun wrongWay() {
    handler.postDelayed({
      // 这个 lambda 隐式持有 Activity 引用
      updateUI() // 可能导致泄漏
    }, 10000)
  }
  
  // 正确示例:使用静态内部类
  private val handler = Handler(Looper.getMainLooper())
  private val runnable = object : Runnable {
    override fun run() {
      // 使用弱引用
      weakRef.get()?.updateUI()
    }
  }
  
  private val weakRef = WeakReference(this)
  
  fun correctWay() {
    handler.postDelayed(runnable, 10000)
  }
  
  override fun onDestroy() {
    handler.removeCallbacksAndMessages(null)
    super.onDestroy()
  }
}

2. Handler 消息优先级

  private val handler = Handler(Looper.getMainLooper())
  
  fun demonstratePriority() {
    // 普通消息
    handler.post {
      Log.d("Priority", "普通消息")
    }
    
    // 异步消息(高优先级)
    val asyncMsg = Message.obtain(handler) {
      Log.d("Priority", "异步消息")
    }
    asyncMsg.isAsynchronous = true
    handler.sendMessage(asyncMsg)
    
    // 插入同步屏障
    val token = handler.postSyncBarrier()
    
    // 屏障后的普通消息被阻塞
    handler.post {
      Log.d("Priority", "被阻塞的普通消息")
    }
    
    // 异步消息可以越过屏障
    val asyncMsg2 = Message.obtain(handler) {
      Log.d("Priority", "越过屏障的异步消息")
    }
    asyncMsg2.isAsynchronous = true
    handler.sendMessage(asyncMsg2)
    
    // 移除屏障
    handler.removeCallbacksAndMessages(token)
  }
}

3. Handler 与协程对比

  private val handler = Handler(Looper.getMainLooper())
  
  // Handler 方式
  fun handlerWay() {
    Thread {
      // 后台工作
      val result = doWork()
      
      handler.post {
        // 回到主线程
        updateUI(result)
      }
    }.start()
  }
  
  // 协程方式
  fun coroutineWay() {
    CoroutineScope(Dispatchers.Main).launch {
      val result = withContext(Dispatchers.IO) {
        doWork()
      }
      updateUI(result)
    }
  }
  
  private fun doWork(): String {
    Thread.sleep(1000)
    return "工作完成"
  }
  
  private fun updateUI(result: String) {
    Log.d("Compare", "更新 UI: $result")
  }
}

4. Handler 性能优化

  private val handler = Handler(Looper.getMainLooper())
  private val messagePool = mutableListOf<Message>()
  
  // 消息复用
  fun optimizedPost() {
    val msg = obtainMessage()
    msg.what = 1
    msg.obj = "数据"
    handler.sendMessage(msg)
  }
  
  private fun obtainMessage(): Message {
    return if (messagePool.isNotEmpty()) {
      messagePool.removeAt(0)
    } else {
      Message.obtain()
    }
  }
  
  private fun recycleMessage(msg: Message) {
    msg.recycle()
    messagePool.add(msg)
  }
}

面试题总结

基础题

  1. Handler 的作用是什么?
  • 线程间通信、定时任务、延迟执行
  1. Handler 的消息机制是怎样的?
  • Looper 循环取消息 → Handler 处理消息
  1. 为什么主线程不会因为 Looper.loop() 而阻塞?
  • 使用 epoll 机制,无消息时进入等待状态

进阶题

  1. 延时消息的原理是什么?
  • 按时间排序,使用 epoll 等待
  1. 消息屏障的作用是什么?
  • 阻塞普通消息,只处理异步消息
  1. 异步消息的使用场景有哪些?
  • VSync 信号、输入事件、系统消息

高级题

  1. Handler 内存泄漏如何避免?
  • 使用静态内部类、弱引用、及时移除
  1. IdleHandler 的使用场景?
  • 启动优化、预加载、清理任务
  1. IntentService 与普通 Service 的区别?
  • 单线程、自动停止、队列处理
  1. Handler 与协程的对比?
  • Handler 更底层、协程更高级抽象