架构师学习--Handler实现UI更新原理及手撸

一、Handler原理

了解其原理首先需要了解一下4个类。

  • Handler

      (1)负责从子线程(或主线程)发送消息到消息队列(通过sendMessage等方法)

      (2)处理消息队列返回的消息对象(通过handlerMessage方法)

  • Message

       消息对象(包含what、obj、当前handler等属性)

  • Looper

       负责从消息队列中不断的取出消息,交给handler处理(通过Looper.loop方法)

  • MessageQueue

       消息队列,存储消息。

1、源码分析,通过ActivityThread.class。进入该类的main方法,代码如下:

public static void main(String[] args) {
       。。。

        Looper.prepareMainLooper();

       。。。
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

2、跟踪prepareMainLooper(),代码如下:

 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

3、进入prepare()方法,代码如下:

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

sThreadLocal之前的handler中已经讲过,它的key就是当前线程,value是Looper对象。所以我们不能重复的调用prepare方法,否则就会抛出异常。另外线程第一次调用该方法就会创建一个Looper对象,保存在threadLocal中,是全局唯一的。

4、进入Looper的构造方法,代码如下:

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

在Looper的构造方法中会创建一个MessageQueue消息队列对象。由于一个线程中Looper只能被初始化一次,所以一个线程对应一个Looper,一个Looper对应一个消息队列MessageQueue

5、当我们发送消息,以调用sendMessage为例,其实无论是哪种发送消息的方式,最终都会进入enQueueMessage方法,代码如下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

这里重点注意 msg.target = this;这里的msg.target就是handler对象。最后通过调用enqueueMessage方法加入到队列中。

6、如何加入到messageQueue中,代码如下:

boolean enqueueMessage(Message msg, long when) {
       

        synchronized (this) {
            。。。
            //1-----------
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;

                //2------------
                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; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

注释1处,全局变量mMessages一开始是为空的,并在注释2处对其赋值成最新的消息。然后当前msg的下一个元素指向mMessages(当取消息的时候就会将mMessages取出来),然后并把当前msg插入到链表中。此时消息等待被消息

7、取消息通过Looper.loop方法,代码如下:

 public static void loop() {

        //1------------拿到当前消息队列
        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 (;;) {

            //2-------------消息队列中取出一条消息
            Message msg = queue.next(); // might block
           
            try {
                
            //3-----------------消息交给handler处理
                msg.target.dispatchMessage(msg);
                
            } finally {
               
            }
            
        }
    }

注释2处就是从队列中取消息,注释3将消息交给当前当前消息的handler处理。这里的 msg.target在enqueueMessage()的时候就已经赋值。最终交给交给回调方法handlerMessage()处理。重点看注释2的next()方法,代码如下:

Message next() {
        
        for (;;) {
           

          //3-------------native层  
           nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
             
                Message prevMsg = null;

            //1-------------------------将全局的消息赋值
                Message msg = mMessages;

                if (msg != null) {
                    if (now < msg.when) {
                        
                    } else {
            //2---------------------返回消息           
                        return msg;
                    }
                } else {
                  
                }

            }

          
        }
    }

首先会获取全局的消息对象,该对象的赋值在第6步已经完成,然后将该消息返回。这里使用死循环。为什么不会导致程序的ANR呢,其实这个方法除了返回message对象,另外就是在调用native层方法对资源进行释放。简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源

二、手撸Handler核心代码

1、创建测试类HandlerTest.java代码如下:

public class HandlerTest {
    @Test
    public void main(){
        //创建looper
        Looper.prepare();
        //接受消息
        Handler handler = new Handler(){
            @Override
            public void handlerMessage(Message message) {
                super.handlerMessage(message);
                System.out.printf("收到消息:%s\n",message.obj.toString());
            }
        };

        //发送消息
        Message msg = new Message();
        msg.obj = "123";
        handler.sendMessage(msg);

        //循环取消息
        Looper.loop();
    }
}

测试方法模拟消息发送和接受的过程。

2、创建Looper.class,代码如下:

public class Looper {
    public static ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
    public static MessageQueue sQueue;

    public Looper() {
        sQueue = new MessageQueue();
    }

    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

    public static Looper myLooper(){
        return sThreadLocal.get();
    }

    public static void loop() {
        Looper looper = myLooper();
        if (looper == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        while (true){
            Message message = sQueue.next();
            if(message != null)
            message.target.dispatchMessage(message);

        }
    }
}

内部实例化一个threadLocal和维持MessageQueue对象的引用。并提供prepare()和loop()方法

3、创建Handler.class,代码如下:

public class Handler {
    private Looper mLooper;
    private MessageQueue mQueue;

    public Handler() {

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.sQueue;
    }

    public void dispatchMessage(Message message) {
        handlerMessage(message);
    }

    public void handlerMessage(Message message){

    }

    public void sendMessage(Message msg) {
        msg.target = this;
        mQueue.enQueueMessage(msg);
    }
}

内部维持Looper对象的引用,并通过looper对象拿到唯一的消息队列mQueue。提供发送消息方法sendMessage()和处理消息方法handlerMessage()。

4、创建消息Message.class。代码如下;

public class Message {
    public int what;
    public Handler target;
    public Object obj;

    @Override
    public String toString() {
        return "Message{" +
                "what=" + what +
                ", target=" + target +
                ", obj=" + obj +
                '}';
    }
}

5、创建消息队列MessageQueue.class。代码如下:

public class MessageQueue {
    private BlockingDeque<Message> mBlockingDeque = new LinkedBlockingDeque<>();


    public Message next() {
        try {
            return mBlockingDeque.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }


    public void enQueueMessage(Message msg) {
        try {
            mBlockingDeque.put(msg);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

采用阻塞队列方式模拟消息的加入队列和取出队列的过程。提供next()和enQueueMessage()方法。

6、测试结果,截图如下:

收到消息,并且队列处于阻塞状态。

 

这里抛出一个经常遇到的面试题:消息机制中Looper.loop()死循环不会阻塞主线程,导致ANR吗?答案是不会阻塞主线程,因为调用loop()方法,会调用MessageQueue的next()方法,这个方法中会调用nativePollOnce()底层方法,当消息阻塞时,会释放cpu资源。一旦有消息需要处理,会通知它立马运转起来。所以android是消息驱动机制的,只有不断的消息循环才能唤醒cpu,真正导致ANR的原因是:其中有消息不能在规定时间内处理,导致其他消息无法处理,而不是死循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值