Android消息机制浅析——面试总结

面试题

  1. 如何将一个Thread线程变成Looper线程?Looper线程有哪些特点?
  2. 简述下Handler、Message、Looper、MessageQueue、Thread的作用,以及他们之间的关系。
  3. 简述消息机制的回调处理过程,怎么保证消息处理机制的唯一性。
  4. 为什么通过postXXX方法可以直接在runnable中更新UI。
  5. 简述下Android消息机制流程。
  6. 创建Handler对象必须先初始化一个Looper对象吗?为什么Handler还提供给我们一个无参构造函数?

一、如何将一个Thread线程变成Looper线程?Looper线程有哪些特点?

答:我们知道Thread是不具备消息循环的,而Looper恰恰弥补了这点,通过Looper.prepare()方法可以将一个Thread线程转换成Looper线程。Looper类管理线程的消息队列和消息循环,具体来说是为一个线程开启一个消息循环,Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。而一个Looper对象可以对应多个线程,比如主线程的mainLooper,供主线程和所属子线程共同使用。

二、简述下Handler、Message、Looper的作用,以及他们之间的关系。

答:Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。

Thread:线程,负责调度整个消息循环,即消息循环的执行场所。 
在Android消息机制中,Handler发送消息到Looper的成员变量MessageQueue中,然后Looper调用loop()方法进行MessageQueue的遍历,然后进行消息的回调处理。

三、简述消息机制的回调处理过程,怎么保证消息处理机制的唯一性。

答:Android消息机制中,Looper类通过loop方法进行消息的遍历,通过loop.target.dispatchMessage()方法进行休息的回调处理。我们知道,一个Message可以被多个Handler发送,Android为了确保消息回调的正确定,在Message类中通过target成员变量保存发送的Handler,以此来确保调用正确的Handler.dispatchMessage()进行消息的分发处理。在dispatchMessage()方法中调用我们重写的handleMessage()方法进行消息的回调处理。

四、为什么通过postXXX方法可以直接在runnable中更新UI以及消息回调流程。

答:我们知道Android的消息机制就是为了解决我们一些无法再子线程中处理UI线程的工作的问题,那么为什么我们通过postXXX发送runnable消息就可以在runnbale中处理UI线程所做的工作呢?首先在消息发送的时候,postXXX系列方法最后也是走的sendMessageAtTime()方法进行处理,在这里使用getPostMessage()方法进行创建一个Message对象,同时将runnable绑定到该message.callback上,在Looper.loop()进行遍历的时候,通过msg.target.dispatchMessage()进行回调处理,该方法首先判断Message.callback是否为null,就是针对Runnable进行处理,如果不为null,则调用

private static void handleCallback(Message message) {
    message.callback.run();
}

进行回调处理工作。

五、简述下Android消息机制流程。

答:1、首先创建Message对象,Message对象用Hanlder target字段存储Hanlder,实现该Message与发送源Handler的关系绑定,因为一个Handler可以发送多个消息,同样一个Message也可以被多个Handler发送,确保最后调用正确的Handler来处理该消息的回调。用Runnable callback存储Runnable。 
2、创建Handler时,会指定Handler内部的Looper成员变量,同时制定成员变量mQueue = looper.mQueue; 
3、Handler通过sendXXMessage()方法发送消息,该系列方法的本质是调用sendMessageAtTime方法,该方法显示判断MessageQueue是否为null,然后调用enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)实现Message与Handler的绑定(msg.target = this),在调用MessageQueue的插入队列方法,形成MessageQueue消息组。该消息组就是Looper内部的mQueue。 
4、通过第3步,Looper内部的MessageQueue就建立了,Looper内部通过loop()方法进行遍历MessageQueue,通过msg.target.dispatchMessage(msg);调用Handler的分发事件实现消息回调的处理。

六. 创建Handler对象必须先初始化一个Looper对象吗?为什么Handler还提供给我们一个无参构造函数?

答:创建Handler必须先初始化一个Looper对象,当我们创建Handler对象时,我们没有指定Looper对象,所以在Handler内部处理过程中会调用Looper.myLooper()进行初始化一个Looper对象,我们看过源码应该知道myLooper()函数处理的内部就是获取本地的ThreadLoacl中的值,所以sThreadLocal.get() will return null unless you’ve called prepare().我们必须先调用prepare()方法进行初始化,在prepare()方法中执行 sThreadLocal.set(new Looper(quitAllowed));为什么我们在主线程中可以直接进行Handler myHandler = new Handler();的操作呢?这是由于主线程已经完成了prepare()方法的调用,所以当我们这样在主线程中创建Handler时,可以直接在postXXX中更新UI操作,换句话说Looper中的mThread绑定的是主线程。如果我们创建Handler时指定的Looper对象不是绑定的主线程,那么我们就无法直接在postXXX中更新UI操作了。

猜你喜欢

转载自blog.csdn.net/suyimin2010/article/details/81151162
今日推荐