面试时说的Android消息机制

来源:互联网 发布:冲压模具设计软件 编辑:程序博客网 时间:2024/06/14 04:27
Android的消息机制
Android的消息机制主要指Handler的运行机制,而Handler的运行又需要底层的MessageQueue和Looper的支撑。

总体流程图

MessageQueue

消息队列,但是内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。

Looper

由于MessageQueue只是一个消息的存储单元,它不能去处理消息,而Looper就填补了这个功能,Looper会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待着。

Handler

主要作用是将一个任务切换到某个指定的线程中去执行。

系统之所以提供Handler,主要原因就是为了解决在子线程中无法访问UI的矛盾。

  • 系统为什么不允许在子线程中访问UI呢?

    • 因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控 件处于不可预期的状态。
  • 那竟然不安全,为什么系统不对UI控件的访问加上锁机制呢?

    • 加锁后会有2个缺点:首先加上锁机制会让UI访问的逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。

第一步:Handler发消息msg(在子线程中发送message,在主线程中创建Handler)

创建Handler完毕后,

  1. 通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理,

  2. 也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中去处理。

其实post方法最终也是通过send方法来完成。

步骤流程: sendMessage(msg)-->sendMessageDelayed(Message msg, long delayMillis)-->sendMessageAtTime(Message msg, long uptimeMillis)-->enqueueMessage(Message msg, long when)

这里的enqueueMessage方法是调用了MessageQueue的enqueueMessage方法

//1.发消息handler.sendMessage(msg);//发消息public final boolean sendMessage(Message msg){    //2 sendMessageDelayed    return sendMessageDelayed(msg, 0);}//2 sendMessageDelayedpublic final boolean sendMessageDelayed(Message msg, long delayMillis){    if (delayMillis < 0) {        delayMillis = 0;    }    //3 sendMessageAtTime    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}//3 sendMessageAtTimepublic boolean sendMessageAtTime(Message msg, long uptimeMillis){    boolean sent = false;    //this 就是 handler mQueue 就是从Looper中取出来的消息队列    MessageQueue queue = mQueue;    if (queue != null) {        //handler发消息 把当前的handler对象绑定到Message对象中        msg.target = this;        // 把消息放到消息队列里,排序 ,如果你调用sendMessage(msg),uptimeMillis = 0        //4 enqueueMessage        sent = queue.enqueueMessage(msg, uptimeMillis);    }    。。。    return sent;} //4 enqueueMessagefinal boolean enqueueMessage(Message msg, long when) {    final boolean needWake;    synchronized (this) {        if (mQuiting) {            RuntimeException e = new RuntimeException(                msg.target + " sending message to a Handler on a dead thread");            Log.w("MessageQueue", e.getMessage(), e);            return false;        } else if (msg.target == null) {            mQuiting = true;        }        msg.when = when;        //Log.d("MessageQueue", "Enqueing: " + msg);        //mMessages 是MessageQueue对象里的属性        Message p = mMessages;        if (p == null || when == 0 || when < p.when) {           //msg 发的消息            msg.next = p;            mMessages = msg;            needWake = mBlocked; // new head, might need to wake up        } else {            Message prev = null;            while (p != null && p.when <= when) {                prev = p;                p = p.next;            }            msg.next = prev.next;            prev.next = msg;            needWake = false; // still waiting on head, no need to wake up        }    }    if (needWake) {        nativeWake(mPtr);//jni    }    return true;}
第二步:当Handler的send方法被调用时,它最后会调用MessageQueue的enqueueMessage方法将这个消息放入消息队列中


消息队列在Android中指的是MessageQueue, MessageQueue主要包含两个操作插入和读取

插入 对应的方法为enqueueMessage

  • enqueueMessage的作用是往消息队列中插入一条消息

读取 对应的方法为next

  • next的作用是从消息队列中取出一条消息并将其从消息队列中移除

从enqueueMessage的实现来看,它的主要操作其实就是单链表的插入操作。

打插入消息的动作并不复杂,无非是在消息链表中找到合适的位置,插入Message节点而已。因为消息链表是按时间进行排序的,所以主要是在比对Message携带的when信息。消息链表的首个节点对应着最先将被处理的消息,如果Message被插到链表的头部了,就意味着队列的最近唤醒时间也应该被调整了,因此needWake会被设为true,以便代码下方可以走进nativeWake()。

从next的实现来看,它是一个无限循环的方法,如果消息队列中没有信息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。

第三步:当Handler发送过来的消息message打入到消息队列后,Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的handleMessage方法就会被调用。

Looper(轮询器),会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。

我们知道,Handler的工作需要Looper,没有Looper的线程就会报错,那么如何为一个线程创建Looper呢?其实很简单,通过Looper.prepare()即可以为当前线程创建一个Looper,接着通过Looper.loop()来开启消息循环


当一个线程运行到某处,准备运作一个Looper时,它必须先调用Looper类的静态函数prepare(),做一些准备工作。说穿了就是创建一个Looper对象,并把它设置进线程的本地存储区(TLS)里。然后线程才能继续调用Looper类的另一个静态函数loop(),从而建立起消息处理循环。示意图如下



* ActivityThread类中初始化,应用程序运行之前执行,main方法如下: //主线程 public static final void main(String[] args) {    。。。    //轮询器的初始化    Looper.prepareMainLooper();    。。。    //轮询器开始取消息    Looper.loop();}

* loop方法

  public static final void loop() {    Looper me = myLooper();    MessageQueue queue = me.mQueue;    while (true) {        //没有消息阻塞        Message msg = queue.next(); // might block        if (msg != null) {            if (msg.target == null) {                // No target is a magic identifier for the quit message.                return;            }            if (me.mLogging!= null) me.mLogging.println(                    ">>>>> Dispatching to " + msg.target + " "                    + msg.callback + ": " + msg.what                    );            msg.target.dispatchMessage(msg);            if (me.mLogging!= null) me.mLogging.println(                    "<<<<< Finished to    " + msg.target + " "                    + msg.callback);            // Make sure that during the course of dispatching the            // identity of the thread wasn't corrupted.            final long newIdent = Binder.clearCallingIdentity();            if (ident != newIdent) {                Log.wtf("Looper", "Thread identity changed from 0x"                        + Long.toHexString(ident) + " to 0x"                        + Long.toHexString(newIdent) + " while dispatching to "                        + msg.target.getClass().getName() + " "                        + msg.callback + " what=" + msg.what);            }            msg.recycle();        }    }}

Looper往消息队列中取消息,其中loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null

  public static final void loop() {    Looper me = myLooper();    MessageQueue queue = me.mQueue;    while (true) {        //没有消息阻塞        //从消息队列里取消息头        Message msg = queue.next(); // might block        if (msg != null) {            ...             打印日志            。。。           处理消息            msg.target.dispatchMessage(msg);            。。            回收消息            msg.recycle();        }    }}  处理消息  msg.target.dispatchMessage(msg);public void dispatchMessage(Message msg) {    if (msg.callback != null) {//runOu......        //处理runOnUithread方法       //1        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;            }        }        //调用handleMessage 主线程        handleMessage(msg);    }}//1.  handleCallback(msg);//2.private final void handleCallback(Message message) {    message.callback.run();//runnable 跟线程没关系}


处理消息过程:
如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg);,这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法处理了。但是这里不同的是,Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功地将代码逻辑切换到指定的线程中去执行了,最后还是由对应的线程Handler调用handlerMessage方法。


从源码中我们可以发现,Handler发送消息的过程仅仅是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理了,最终消息由Looper交由Handler处理。即Handler的dispatchMessage方法会被调用,这时Handler就进入了处理消息的阶段。



本文参考了书籍: 《Android开发技术探索》
参考文章:http://my.oschina.net/youranhongcha/blog/492591

1 0