前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >笔记:安卓App消息处理机制

笔记:安卓App消息处理机制

作者头像
用户1172465
发布2018-01-08 14:35:58
1.1K0
发布2018-01-08 14:35:58
举报
文章被收录于专栏:everhad

内容简述

类似Binder机制,MessageQueue、Looper也有底层的C++实现,涉及文件管道和驱动等。 以下仅从Java层的Looper、Handler和MessageQueue等相关类型的源码来分析线程消息处理的机制。

MessageQueue的创建

Looper用来创建和启动消息队列。 Looper对象和线程绑定是通过ThreadLocal实现的。 它提供了各种getter方便获取线程关联的MessageQueue和Looper对象。

代码语言:javascript
复制
Looper.prepare();
Looper.prepareMainLooper();

线程通过执行Looper.prepare()来创建关联的MessageQueue。

主线程则调用prepareMainLooper()来创建主线程关联的Looper,方便其它线程向主线程发消息。

Looper.prepare()

它新建了Looper对象,以ThreadLocal存储它,所以和当前线程绑定。

构造函数中创建了消息队列:

代码语言:javascript
复制
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

MessageQueue的创建

代码语言:javascript
复制
// MessageQueue.java
private long mPtr;

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

nativeInit()调用C++代码创建底层的C++MessageQueue对象。 有了MessageQueue对象以后,接着需要开启消息循环,使用关联的Handler来发送、处理消息了。

消息循环

MessageQueue有点像一个阻塞队列,它提供MessageQueue.next()用来从队列中取出一个Message对象。 若没有消息则调用会阻塞。

Looper.loop()

Looper.loop()用来开启对MessageQueue的取消息操作的无限循环

代码语言:javascript
复制
public static void loop() {
    final Looper me = myLooper();
    ...
    final MessageQueue queue = me.mQueue;
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
        msg.recycleUnchecked();
    }
}

可以看到,loop()的操作就是无限从queue中调用next()获取一个新Message对象,然后执行dispatchMessage来处理它。

MessageQueue.next()

nativePollOnce()用来检查队列中是否有新的消息。

参数nextPollTimeoutMillis表示在暂无消息时此次检查过程需要休眠的时间:

  • 等于-1:表示无限等待,直到被其它线程唤醒。
  • 等于 0:继续循环检查队列。
  • 大于 0:以nextPollTimeoutMillis的毫米数等待。

使用for (;;) 无限循环检查是否有消息,直到返回一个Message对象。 若当前MessageQueue正在退出,则返回null作为标识。

代码语言:javascript
复制
Message next() {
    ...
    int nextPollTimeoutMillis = 0;
    for (;;) {
        ...
        nativePollOnce(ptr, nextPollTimeoutMillis);
        ...
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message msg = mMessages;
            ...
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    ...
                    mMessages = msg.next;
                    msg.next = null;
                    ...
                    return msg;
                }
            } else {
                // 继续循环等待消息
                nextPollTimeoutMillis = -1;
            }

            ...
            // 每次执行next()时,若当前没有消息可处理,那么就找到所有的idle handlers,回调它们
            ...
        }

        // 回调idle handlers. 每次调用next()只会执行一次
        ...

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

每次调用next()时,第一次检查如果发现没有要出来的消息,就一次性调用所有在注册了的IdleHandler回调对象。

发送消息

MessageQueue和Looper对象都是和某个线程关联的。 向一个线程发送消息,通过和它绑定的一个Handler对象进行。 执行创建Handler对象的的线程,Handler对象和此线程绑定。

Handler的创建

Handler对象的构造函数中,将当前线程关联的Looper和MessageQueue保存到其字段中。

代码语言:javascript
复制
public Handler(Callback callback, boolean async) {
    ...

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Handler.sendMessageDelayed

每个发送到线程关联的MessageQueue中的Message对象,主要字段有:

  • long when字段表示消息预期被处理的时间;
  • int what表示消息的动作;
  • Object obj可以携带额外的数据。

发送消息的一般形式为:

代码语言:javascript
复制
boolean sendMessageAtTime(Message msg, long uptimeMillis);
boolean sendMessageDelayed(Message msg, long delayMillis);

boolean sendEmptyMessageAtTime(int what, long uptimeMillis);
boolean sendEmptyMessageDelayed(int what, long delayMillis);

若不指定when那么它为当前时间,之后被Looper取出后立即执行;

sendMessage()中将Handler Message.target设置为自身,最执行Handler绑定的队列的MessageQueue.enqueue()方法。

代码语言:javascript
复制
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

MessageQueue.enqueueMessage

字段Message Message.next用来指向Message链表中的下一个对象。 MessageQueue.mMessages字段指向了它拥有的消息链表的头结点。 mMessages中的消息根据其when的时间排列,时间近的在前面。 enqueueMessage()在添加消息时将它放到mMessages的合适的位置。

代码语言:javascript
复制
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        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;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            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;
}

mBlocked记录当前队列是否处于休眠? 在next()时若最近要处理的消息的when还未到或队列空时,则在检查队列是否空时会主动休眠一段时间。

若新的Message被添加到链表头,且它的when时间到了,那么就唤醒Looper继续执行next(),获取此Message然后处理它。

代码语言:javascript
复制
if (needWake) {
    nativeWake(mPtr);
}

消息的处理

在Looper.loop()中,每当获取新的消息后:

代码语言:javascript
复制
Message msg = queue.next(); // might block
...
msg.target.dispatchMessage(msg);

target就是发送Message的Hander。 Hander发送消息到其绑定的MessageQueue中。 可见,相关的Looper、MessageQueue、Handler都是和同一个线程关联的。

从next()的执行在Looper.loop()的循环中进行可知: Hander.dispatchMessage()的调用就是在Looper关联的线程中进行。

Hander.dispatchMessage

代码语言:javascript
复制
public class Handler {
 final Callback mCallback;

  /**
   * Callback interface you can use when instantiating a Handler to avoid
   * having to implement your own subclass of Handler.
   *
   * @param msg A {@link android.os.Message Message} object
   * @return True if no further handling is desired
   */
  public interface Callback {
      public boolean handleMessage(Message msg);
  }

  public void dispatchMessage(Message msg) {
     if (msg.callback != null) {
         handleCallback(msg);
     } else {
         if (mCallback != null) {
             if (mCallback.handleMessage(msg)) {
                 return;
             }
         }
         handleMessage(msg);
     }
  }

  /**
   * Subclasses must implement this to receive messages.
   */
  public void handleMessage(Message msg) {
  }
}

上面的Message.callback是一个Runnable。 Handler.mCallback是创建它时指定的一个回调对象。 handleMessage()就是子类重写处理自定义消息的地方。

循环和退出

Looper.loop()执行后,线程“阻塞”,不断从关联的MessageQueue中取出消息并处理。 其它线程或在处理某个消息的逻辑中,可以调用Looper.quit()退出当前线程的消息循环。

代码语言:javascript
复制
// Looper.java
final MessageQueue mQueue;
...
public void quit() {
    mQueue.quit(false);
}

它执行了MessageQueue.quit()。

代码语言:javascript
复制
void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

这里设置mQuitting为true。 之后下次调用next()时,返回null告知Looper.loop()退出循环。 线程的消息循环结束。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-01-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 内容简述
  • MessageQueue的创建
    • Looper.prepare()
      • MessageQueue的创建
      • 消息循环
        • Looper.loop()
          • MessageQueue.next()
          • 发送消息
            • Handler的创建
              • Handler.sendMessageDelayed
                • MessageQueue.enqueueMessage
                • 消息的处理
                  • Hander.dispatchMessage
                  • 循环和退出
                  相关产品与服务
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档