Handler消息机制

2020-04-30  本文已影响0人  寒冬_腊月

概念

Android的UI更新是单线程模型,只能在主线程上操作,在子线程上就要通过使用Handler来进行更新UI的操作
Handler的运行基于MessageQueue(单链表)和Looper, MessageQueue顾名思义是消息队列。但其实是一个单项链表结构来存储信息Message的。而Looper则是不断去读取消息队列MessageQueue中的信息,Handler的工作主要是发送和接收消息,一个线程只有一个Looper

Looper

用于对线程使用消息循环的一个类,在一个线程中调用prepare()方法是为了运行loop, 再调用loop()处理消息直到loop停止

子线程中创建Handler

new Thread(new Runnable() {  
   public void run() { 
       //创建消息队列和保存当前的线程
       Looper.prepare();  
       mHandler = new Handler(){  
       @Override  
       public void handleMessage(Message msg) {  
               //在这里处理传入的消息 
           }  
        };  
        //遍历消息队列
        Looper.loop();  
    };  
 }).start();  

为什么子线程更新UI会报错

在子线程中更新UI会报下面错误

Only the original thread that created a view hierarchy can touch its views.

为什么会报这个,在ViewRootImp.java类,中会判断当前线程和主线程
这个类是在onResume之后创建的

void checkThread() {
    if (mThread != Thread.currentThread()) {
      throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
    }
}

在主线程中更新UI,就可以避免多线程操作UI,导致不同步的一些问题

如何处理延迟消息的

  1. 消息是通过MessageQueen中的enqueueMessage()方法加入消息队列中的,并且它在放入中就进行好排序,链表头的延迟时间小,尾部延迟时间最大

  2. Looper.loop()通过MessageQueue中的next()去取消息

  3. next()中如果当前链表头部消息是延迟消息,则根据延迟时间进行消息队列会阻塞,不返回给Looper message,知道时间到了,返回给message

  4. 如果在阻塞中有新的消息插入到链表头部则唤醒线程

  5. Looper将新消息交给回调给handler中的handleMessage后,继续调用MessageQueen的next()方法,如果刚刚的延迟消息还是时间未到,则计算时间继续阻塞

Looper为什么不会阻塞主线程

因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。

参考

掘金

上一篇下一篇

猜你喜欢

热点阅读