图片 19

Android消息处理机制(Handler、Looper、MessageQueue与Message)

Android的音信管理机制入眼分为多个部分:


  • 创办音信队列
  • 新闻循环
  • 信息发送
  • 信息管理

简介

新闻使得是生龙活虎种进度/线程的运作方式,内部照旧外界的音讯事件被放置进度/线程的音讯队列中按序管理是今后的操作系统广泛运用的机制.Android也是运用了新闻使得的机制来拍卖各样外界按钮,触屏,系统Intent,广播事件等音信.

Android的音讯队列是线程相关的,每运转二个线程,都得以在内部创设一个新闻队列,然后在新闻队列中不断循环检查是还是不是有新的音信须要管理,假如有,则对该音信进行拍卖,若无,线程就进来休眠状态直到有新的音信需求管理甘休.


Android是新闻使得的,达成消息使得有几个因素:

重大涉嫌四个类:

数据模型

Android中与消息机制相关的类保护有Looper,MessageQueue,Handler,Message,相关的代码主要在偏下文件中:

  • frameworks/base/core/java/android/os/Looper.java
  • frameworks/base/core/java/android/os/Message.java
  • frameworks/base/core/java/android/os/MessageQueue.java
  • frameworks/base/core/java/android/os/Handler.java
  • frameworks/base/core/jni/android_os_MessageQueue.cpp
  • system/core/libutils/Looper.cpp
  • Looper
    Looper对象是用来创制新闻队列并进入信息循环管理的.每种线程只好有二个Looper对象,同期对应着二个MessageQueue,发送到该线程的音信都将存放在该队列中,并由Looper循环处理。Android默许只为主线程State of Qatar(UI线程卡塔尔(قطر‎创造了Looper,所以当大家新建线程必要运用新闻队列时必须手动创制Looper.
  • MessageQueue
    MessageQueue即信息队列,由Looper创设管理,多少个Looper对象对应叁个MessageQueue对象.
  • Handler
    Handler是音信的吸纳与管理者,Handler将Message加多到音讯队列,同期也通过Handler的回调方法handleMessage(卡塔尔管理相应的音信.一个Handler对象只好关联七个Looper对象,但八个Handler对象足以提到到同二个Looper.暗中认可意况下Handler会关联到实例化Handler线程的Lopper,也足以通过Handler的构造函数的Looper参数钦赐Handler关联到有个别线程的Looper,即发送消息到有些内定线程并在该线程中回调Handler管理该新闻.
  • Message
    Message是音讯的载体,Parcelable的派生类,通过其成员变量target关联到Handler对象.

它们之间关系如下图示:

图片 1

Handler,Looper,MessageQueue之间的关系

在代码中我们常常如下使用线程的消息机制:

class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
}

  1. 新闻的意味:Message
  2. 音讯队列:MessageQueue
  3. 消息循环,用于循环抽取消息进行管理:Looper
  4. 新闻处理,音信循环从音讯队列中收取音讯后要对音讯举行拍卖:Handler
  • MessageQueue
  • Looper
  • Handler

线程新闻队列的始建

线程的消息队列通过Looper创造并维护的,主线程中调用Looper.prepareMainLooper(卡塔尔国,其余子线程中调用Looper.prepare(卡塔尔(قطر‎来制造音讯队列.三个线程数拾回调用prepareMainLooper(卡塔尔(قطر‎或prepare(卡塔尔(قطر‎将会抛出相当.

在介绍信息队列创造在此之前,首先了然一下Looper与MessageQueue,再看新闻队列成立的流程.

  1. Looper类的严重性成员变量与措施如下:

public final class Looper {
      static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
      private static Looper sMainLooper;  
      final MessageQueue mQueue;
      final Thread mThread;

      public static void prepare() {...}
      private static void prepare(boolean quitAllowed) {...}
      public static void prepareMainLooper() {...}
      public static Looper getMainLooper() {...}
      public static void loop() {...}
}
  • sThreadLocal是静态成员变量,用于保存线程私有的Looper对象
  • sMainLooper是主线程的Looper对象.在prepareMainLooper(State of Qatar中赋值,可因而调用getMainLooper获取
  • mQueue即音信队列,在Looper布局函数中开首化
  • mThread即Looper所在的线程
  1. MessageQueue类的首要性成员变量与艺术如下:

public final class MessageQueue {
      private final boolean mQuitAllowed;
      private long mPtr;
      Message mMessages;

      MessageQueue(boolean quitAllowed) {...}
      boolean enqueueMessage(Message msg, long when) {...}
      Message next() {...}
}
  • mQuitAllowed代表是或不是同意退出音信循环,主线程中默以为false,子线程私下认可false
  • mPtr保存的是NativeMessageQueue的地址,通过该地点就能够找到java层MessageQueue所对应native的MessageQueue.
  • mMessages即音信队列,通过mMessages能够遍历整个音信队列
  1. 信息队列的创导:
    新闻队列的创始从Looper.prepare(卡塔尔/Looper.prepareMainLooper(卡塔尔国开首

public static void prepare() {
      prepare(true);
}

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

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

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

透过调用prepare(卡塔尔(قطر‎或prepareMainLooper(State of Qatar创造Looper对象,然后保留到sThreadLocal中,sThreadLocal是模板类ThreadLocal<T>,它通过线程ID与对象关系的艺术落到实处线程本地存款和储蓄成效.那样归入sThreadLocal对象中的Looper对象就与成立它的线程关联起来了.所以可以从sThreadLocal中拿走到保存的Looper对象:

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

主线程的Loopper对象保存在sMainLooper,能够经过getMainLooper获取

public static Looper getMainLooper() {
      synchronized (Looper.class) {
          return sMainLooper;
      }
}

成立Looper相同的时间会成立Looper关联的MessageQueue并赋值给成员变量mQueue,接下去再看new
MessageQueue(quitAllowed卡塔尔国的进度:

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

能够看看,直接调用了nativeInit(State of Qatar.这几个JNI方法定义在android_os_MessageQueue.cpp

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
      NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
      if (!nativeMessageQueue) {
          jniThrowRuntimeException(env, "Unable to allocate native queue");
          return 0;
      }

      nativeMessageQueue->incStrong(env);
      return reinterpret_cast<jlong>(nativeMessageQueue);
}

nativeInit(卡塔尔中率先创制了nativeMessageQueue,然后又将nativeMessageQueue的地址赋值给java层的mPtr,所以java层的MessageQueue就足以通过mPtr找到nativeMessageQueue了.
再看new NativeMessageQueue(State of Qatar进程,NativeMessageQueue的布局如下:

NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
      mLooper = Looper::getForThread();
      if (mLooper == NULL) {
          mLooper = new Looper(false);
          Looper::setForThread(mLooper);
      }
}

它首先通过Looper::getForThread(卡塔尔判断当前线程是或不是已开立过Looper对象,若无则创立.注意,这一个Looper对象是得以实现在JNI层的,与地点Java层的Looper是不等同的,然而也是呼应的关系.JNI层的Looper对象的开创进度是在Looper.cpp中贯彻的.

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd = eventfd(0, EFD_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd.  errno=%d", errno);

    AutoMutex _l(mLock);
    rebuildEpollLocked();
}

创制eventfd并赋值给mWake伊芙ntFd,在原先的Android版本上,这里开创的是pipe管道.eventfd是较新的API,被看做八个事变等待/响应,完结了线程之间事件通告.

void Looper::rebuildEpollLocked() {
    // Close old epoll instance if we have one.
    if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
        ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
        close(mEpollFd);
    }

    // Allocate the new epoll instance and register the wake pipe.
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance.  errno=%d",
            errno);

    for (size_t i = 0; i < mRequests.size(); i++) {
        const Request& request = mRequests.valueAt(i);
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);

        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
        if (epollResult < 0) {
            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set, errno=%d",
                    request.fd, errno);
        }
    }
}

rebuildEpollLocked中通过epoll_create创制了三个epoll专用的文书描述符,EPOLL_SIZE_HINT表示mEpollFd上能监督的最大文件汇报符数.最终调用epoll_ctl监察和控制mWakeEventFd文件呈报符的EPOLLIN事件,即当eventfd中有内容可读时,就提示当前正在等候的线程.

C++层的这一个Looper对象创立好了未来,就回来到JNI层的NativeMessageQueue的构造函数,再回来到Java层的音讯队列MessageQueue的创始进度,最终从Looper的结构函数中重临.线程音信队列的创办进度也就此完成.

计算一下:

  • 率先在Java层创制了二个Looper对象,然后创设MessageQueue对象mQueue,步入MessageQueue的创始进度
  • MessageQueue在JNI层创建了贰个NativeMessageQueue对象,并将这些目的保存在MessageQueue的分子变量mPtr中
  • 在JNI层,创设了NativeMessageQueue对象时,会创制了一个Looper对象,保存在JNI层的NativeMessageQueue对象的积极分子变量mLooper中,这几个目的的机能是,当Java层的消息队列中绝非消息时,就使Android应用程序线程步向等待状态,而当Java层的音信队列中来了新的音信后,就唤醒Android应用程序的线程来处理那么些音讯
  • 关于java层与JNI层的Looper,MessageQueue对象足以这么敞亮,java层的Looper,MessageQueue重要达成了音信队列发送管理逻辑,而JNI层的首要达成是线程的等候/唤醒.在逻辑上她们还是各样对应的关联,只但是侧器重不相同.
![](https://github.com/XRobinHe/Resource/blob/master/blog/image/Looper_MessageQueue.png?raw=true)
java与jni层Looper,MessageQueue关系

平时大家最常使用的正是Message与Handler了,假诺使用过HandlerThread可能自个儿达成相像HandlerThread的东西恐怕还有也许会接触到Looper,而MessageQueue是Looper内部动用的,对于专门的工作的SDK,大家是无助实例化并使用的(布局函数是包可知性)。

Android应用程序每运转贰个线程,都为其成立三个音信队列,然后步入到三个特别循环之中。然后不断检查队列中是还是不是有新音信须要管理。若无,线程就能够进去睡眠状态,反之会对新闻进行分发管理。

线程音讯队列的巡回

当线程新闻队列创制完结后,即步入消息队列循环管理进程中,Android音信队列的巡回通过Loop.Loop(State of Qatar来促成,整个流程如下图示.

图片 2

新闻队列循环流程

下边具体来看具体解析

public static void loop() {
      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 (;;) {
          Message msg = queue.next(); // might block
          if (msg == null) {
              // No message indicates that the message queue is quitting.
              return;
          }

          ...
          msg.target.dispatchMessage(msg);
          ...
      }
}

进去loop前,首先通过myLooper(卡塔尔国得到前面创制的Looper对象,若是为null将会抛出非常,那也正是干吗必得在Looper.loop(卡塔尔国在此之前调用Looper.prepare(卡塔尔(قطر‎只怕Looper.prepareMainLooper(卡塔尔国的案由.接下来通过me.mQueue获得MessageQueue对象,而后步向到尽头循环管理中.在循环中经过queue.next(卡塔尔(قطر‎从队列中取音讯,再调用msg.target.dispatchMessage(msg卡塔尔国管理.上边看一下queue.next(卡塔尔(قطر‎流程.

Message next() {
      final long ptr = mPtr;
      if (ptr == 0) {
          return null;
      }

      int pendingIdleHandlerCount = -1;
      int nextPollTimeoutMillis = 0;
      for (;;) {
          if (nextPollTimeoutMillis != 0) {
              Binder.flushPendingCommands();
          }

          nativePollOnce(ptr, nextPollTimeoutMillis);

          synchronized (this) {
              final long now = SystemClock.uptimeMillis();
              Message prevMsg = null;
              Message msg = mMessages;
              if (msg != null && msg.target == null) {
                  do {
                      prevMsg = msg;
                      msg = msg.next;
                  } while (msg != null && !msg.isAsynchronous());
              }
              if (msg != null) {
                  if (now < msg.when) {
                      nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                  } else {
                      mBlocked = false;
                      if (prevMsg != null) {
                          prevMsg.next = msg.next;
                      } else {
                          mMessages = msg.next;
                      }
                      msg.next = null;
                      if (false) Log.v("MessageQueue", "Returning message: " + msg);
                      return msg;
                  }
              } else {
                  nextPollTimeoutMillis = -1;
              }

              if (mQuitting) {
                  dispose();
                  return null;
              }

              if (pendingIdleHandlerCount < 0
                      && (mMessages == null || now < mMessages.when)) {
                  pendingIdleHandlerCount = mIdleHandlers.size();
              }
              if (pendingIdleHandlerCount <= 0) {
                  mBlocked = true;
                  continue;
              }

              if (mPendingIdleHandlers == null) {
                  mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
              }
              mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
          }

          for (int i = 0; i < pendingIdleHandlerCount; i++) {
              final IdleHandler idler = mPendingIdleHandlers[i];
              mPendingIdleHandlers[i] = null;

              boolean keep = false;
              try {
                  keep = idler.queueIdle();
              } catch (Throwable t) {
                  Log.wtf("MessageQueue", "IdleHandler threw exception", t);
              }

              if (!keep) {
                  synchronized (this) {
                      mIdleHandlers.remove(idler);
                  }
              }
          }

          pendingIdleHandlerCount = 0;
          nextPollTimeoutMillis = 0;
      }
}

先看一下发端定义的2个变量的含义,pendingIdleHandlerCount表示消息队列空闲音讯微型机(IdleHandler卡塔尔国的个数,nextPollTimeoutMillis表示还未音信处理时,线程需睡眠等待的时间.nativePollOnce将会睡觉等待nextPollTimeout米尔is时间.从nativePollOnce再次回到后,再从音讯队列中取新闻,如果未有此外音讯,那么nextPollTimeoutMillis赋值为-1,表示下贰遍nativePollOnce无界定等待直到别的线程把它唤醒.假若取到音讯,比较音讯管理的时间与如今时刻,假诺新闻管理的时刻未到(now
<
msg.when卡塔尔(قطر‎,那么合算nextPollTimeoutMillis,等下壹次时间届时再管理.如若音信管理时间已到,那么抽出音讯重返到Looperde的loop中管理.其余如果当前还未新闻管理时,会回调注册的IdleHandler.
下边继续解析nativePollOnce.

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}

最终nativePollOnce调用的JNI层Looper的pollOnce

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        ...
        if (result != 0) {
            ...
            return result;
        }

        result = pollInner(timeoutMillis);
    }
}

在pollOnce中再三的循环调用pollInner来检查线程是还是不是有新音信供给管理.假如有新音讯管理恐怕timeoutMillis时间到,则赶回到java层MessageQueue的next(卡塔尔国继续实行.

int Looper::pollInner(int timeoutMillis) {
    ...
    int result = POLL_WAKE;

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ...
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
          ...
        }
    }
    ...
    return result;
}

epoll_wait会监听前边创制的epoll实例的文书陈述符上的IO读写事件,假若文件陈述上从不IO事件现身,那么则等待timeoutMillis延时,检查实验到EPOLLIN事件即文件陈说符上产生了写事件,随后调用awoken读出多少,以便选择新的数据.

void Looper::awoken() {
    uint64_t counter;
    TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}

在awoken中读出多少.然后一步步回到到java层的MessageQueue继续新闻管理.


咱俩一向触及到的Looper、Message、Handler都以用JAVA实现的,Android做为基于Linux的系列,底层用C、C++实现的,并且还大概有NDK的存在,音信使得的模子怎么大概只设有于JAVA层,实际上,在Native层存在与Java层对应的类如Looper、MessageQueue等。

下边根据上边所说的张开详述。

线程消息的出殡

新闻的发送是经过Handler来实行的,上面大家从new
Handler(卡塔尔(قطر‎初叶,一步步剖判音讯的出殡和安葬进程
第风华正茂看一下Handler类的重大数据成员与方式:

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

      public Handler() {...}
      public Handler(Looper looper, Callback callback) {...}

      private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {...}
      public void handleMessage(Message msg) {...}

      public final boolean sendMessage(Message msg){...}
      public final boolean sendEmptyMessage(int what){...}
      public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...}
      public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...}
      public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...}
      ...
      public final boolean post(Runnable r){...}
      public final boolean postAtFrontOfQueue(Runnable r){...}
      public final boolean postAtTime(Runnable r, long uptimeMillis){...}
      public final boolean postDelayed(Runnable r, long delayMillis){...}
}
  • mQueue
    handler对应的MessageQueue对象,通过handler发送的消息都将插入到mQueue队列中
  • mLooper
    handler对应的Looper对象,假使创造Handler前还未实例化Looper对象将抛出万分.

Handler是与Looper对象相关联的,大家创设的Handler对象都会波及到某生龙活虎Looper,私下认可景况下,Handler会关联到创立Handler对象所在线程的Looper对象,也可因而Handler的结构函数来钦定关联到的Looper.Handler发送音信有二类接口,post类与send类,日常send类用来发送古板带音讯ID的音讯,post类用来发送带音讯管理格局的音讯.

上边来看音信发送的现实流程

图片 3

音信发送流程

Handler或Post类方法最终都会调用enqueueMessage将新闻发送到消息队列

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

Message的分子变量target赋值为this,即关联到handler.然后持续调用MessageQueue的enqueueMessage方法

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

      /// M: Add message protect mechanism @{
      if (msg.hasRecycle) {
          Log.wtf("MessageQueue", "Warning: message has been recycled. msg=" + msg);
          return false;
      }
      /// Add message protect mechanism @}

      synchronized (this) {
          if (mQuitting) {
              IllegalStateException e = new IllegalStateException(
                      msg.target + " sending message to a Handler on a dead thread");
              Log.w("MessageQueue", 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;
}

MessageQueue中的enqueueMessage首要办事是将message插入到行列,然后依照景况推断是或不是合宜调用nativeWake唤醒指标线程.当前队列为空或然插入消息处理时间延时为0也许管理时间低于队头管理时间时,信息被插入到尾部,不然按期间遍历插入到相应地方,并设置needWake标识,needWake是基于mBlocked来推断的,mBlocked记录了近些日子线程是还是不是处在睡眠情况,假如新闻插入队头且线程在睡眠中,neeWake为true,调用nativeWake唤醒指标线程.

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
      NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
      return nativeMessageQueue->wake();
}

void NativeMessageQueue::wake() {
      mLooper->wake();
}

void Looper::wake() {
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

nativeWake最终会调用到jni层的Looper对象的wake方法中,Looper
wake方法的兑现特简单,即向mWake伊芙ntFd写入一个uint64_t,那样目的线程就能够因为mWakeEventFd爆发的IO事件而唤醒.音信的出殡流程就此停止.


 初阶化新闻队列

首先来看一下借使多个线程想完毕消息循环应该如何做,以HandlerThread为例:

图片 4

public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
} 

图片 5

驷不比舌是品绿标记的两句,首先调用prepare开端化MessageQueue与Looper,然后调用loop步入新闻循环。先看一下Looper.prepare。

图片 6

public static void prepare() {
    prepare(true);
}

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

图片 7

重载函数,quitAllowed默感到true,从名字可以看出来正是音信循环是或不是能够脱离,暗中认可是可脱离的,Main线程(UI线程)最早化新闻循环时会调用prepareMainLooper,传进去的是false。使用了ThreadLocal,每一种线程能够初叶化多个Looper。

再来看一下Looper在开始化时都做了如何:

图片 8

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

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

图片 9

在Looper开头化时,新建了一个MessageQueue的目的保存了在成员mQueue中。MessageQueue的布局函数是包可知性,所以大家是束手旁观直接使用的,在MessageQueue最先化的时候调用了nativeInit,那是四个Native方法:

图片 10

static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return;
    }

    nativeMessageQueue->incStrong(env);
    android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}

static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
        NativeMessageQueue* nativeMessageQueue) {
    env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
             reinterpret_cast<jint>(nativeMessageQueue));
}

图片 11

在nativeInit中,new了四个Native层的MessageQueue的指标,并将其地方保存在了Java层MessageQueue的积极分子mPtr中,Android中有无数这样的兑现,多个类在Java层与Native层都有落到实处,通过JNI的GetFieldID与SetInt菲尔德把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,举个例子Parcel。

再看NativeMessageQueue的实现:

图片 12

NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

图片 13

在NativeMessageQueue的布局函数中拿到了三个Native层的Looper对象,Native层的Looper也使用了线程本地存款和储蓄,注意new
Looper时传入了参数false。

图片 14

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    int wakeFds[2];
    int result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
            errno);

    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
            errno);

    // Allocate the epoll instance and register the wake pipe.
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
            errno);
}

图片 15

Native层的Looper使用了epoll。初叶化了三个管道,用mWakeWritePipeFd与mWakeReadPipeFd分别保存了管道的写端与读端,并监听了读端的EPOLLIN事件。注意下早先化列表的值,mAllowNonCallbacks的值为false。

mAllowNonCallback是做什么样的?使用epoll仅为了监听mWakeReadPipeFd的平地风波?其实Native
Looper不仅能监听那三个描述符,Looper还提供了addFd方法:

int addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data);
int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);

fd表示要监听的叙说符。ident表示要监听的平地风波的标志,值必需>=0可能为ALOOPERAV4_POLL_CALLBACK(-2卡塔尔(قطر‎,event表示要监听的平地风波,callback是事件产生时的回调函数,mAllowNonCallbacks的效果就在于此,当mAllowNonCallbacks为true时允许callback为NULL,在pollOnce中ident作为结果回到,否则不准callback为空,当callback不为NULL时,ident的值会被忽视。依旧从来看代码方便精通:

图片 16

int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
#if DEBUG_CALLBACKS
    ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
            events, callback.get(), data);
#endif
    if (!callback.get()) {
        if (! mAllowNonCallbacks) {
            ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
            return -1;
        }
        if (ident < 0) {
            ALOGE("Invalid attempt to set NULL callback with ident < 0.");
            return -1;
        }
    } else {
        ident = ALOOPER_POLL_CALLBACK;
    }

    int epollEvents = 0;
    if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
    if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;

    { // acquire lock
        AutoMutex _l(mLock);

        Request request;
        request.fd = fd;
        request.ident = ident;
        request.callback = callback;
        request.data = data;

        struct epoll_event eventItem;
        memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
        eventItem.events = epollEvents;
        eventItem.data.fd = fd;

        ssize_t requestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.add(fd, request);
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.replaceValueAt(requestIndex, request);
        }
    } // release lock
    return 1;
}

图片 17

假定callback为空会检查mAllowNonCallbacks看是不是同意callback为空,假诺允许callback为空还有大概会检查实验ident是还是不是>=0。如若callback不为空会把ident的值赋值为ALOOPE大切诺基_POLL_CALLBACK,不管传进来的是哪些值。

接下去把传进来的参数值封装到一个Request布局体中,并以描述符为键保存到一个KeyedVector
mRequests中,然后通过epoll_ctl加多或沟通(若是那一个描述符以前有调用addFD加多监听)对那个描述符事件的监听。

类图:

  图片 18

创造音信队列

整整创制进度涉及到八个类:MessageQueue 和
Looper。它们在C++层有三个照顾的类:NativeMessageQueue和Looper。其涉嫌如下图所示:

          +------+    +------------+  +------------------+  +--------------+                    
          |Looper|    |MessageQueue|  |NativeMessageQueue|  |Looper(Native)|                    
          +--+---+    +------+-----+  +---------+--------+  +-------+------+                    
             |               |                  |                   |                           
             |               |                  |                   |                           
+-------------------------------------------------------------------------------+               
|[msg loop]  |   next()      |                  |                   |           |               
|            +------------>  |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               | nativePollOnce() |                   |           |               
|            |               |    pollOnce()    |                   |           |               
|            |               +----------------> |                   |           |               
|            |               |                  |                   |           |              
|            |               |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               |                  |     pollOnce()    |           |               
|            |               |                  +-----------------> |           |               
|            |               |                  |                   |           |               
|            |               |                  |                   | epoll_wait()              
|            |               |                  |                   +--------+  |               
|            |               |                  |                   |        |  |               
|            |               |                  |                   |        |  |               
|            |               |                  |                   | <------+  |               
|            |               |                  |                   | awoken()  |               
|            +               +                  +                   +           |               
|                                                                               |               
|                                                                               |               
+-------------------------------------------------------------------------------+

开创进程如下所示:

  1. Looper的prepare大概prepareMainLooper静态方法被调用,将一个Looper对象保存在ThreadLocal里面。
  2. Looper对象的初始化方法里,首先会新建八个MessageQueue对象。
  3. MessageQueue对象的最初化方法通过JNI开头化C++层的NativeMessageQueue对象。
  4. NativeMessageQueue对象在开创进度中,会起初化贰个C++层的Looper对象。
  5. C++层的Looper对象在开创的进程中,会在在那之中创建二个管道(pipe),并将这一个管道的读写fd都封存在mWakeReadPipeFd和mWakeWritePipeFd中。
    接下来新建二个epoll实例,并将三个fd注册进去。
  6. 利用epoll的机制,可以做到当管道未有音信时,线程睡眠在读端的fd上,当其他线程往管道写多少时,本线程便会被唤醒以进行新闻管理。

线程音讯的管理

在这里从前方的分析能够驾驭,当线程未有音信供给管理时,会在c++层Looper对象的pollInner中跻身睡眠等待,当有新音讯唤醒该目的线程时或那延时时间到,实践流程将本着pollInner调用路径平昔再次回到,直到java层Looper类的loop.

图片 19

音信管理流程

loop中校调用msg.target.dispatchMessage(msg卡塔尔(قطر‎管理音信,这里的msg.target就是上面enqueueMessage中所赋值的handler,即步入handler的dispatchMessage处理音信

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

dispatchMessage实行新闻管理,先检查是否有设置msg.callback,若是有则实践msg.callback管理新闻,若无则三番两次推断mCallback的实行,最终才是handleMessage管理.


拜会我的博客

发送音讯

由此Looper.prepare起首化好音讯队列后就能够调用Looper.loop踏入新闻循环了,然后我们就足以向新闻队列发送新闻,音信循环就能抽出音信进行管理,在看音讯管理此前,先看一下音信是怎么被加多到音讯队列的。

在Java层,Message类表示三个消息对象,要发送新闻首先将在先获得二个新闻对象,Message类的构造函数是public的,不过不提出直接new
Message,Message内部保存了二个缓存的音讯池,大家得以用obtain从缓存池拿到叁个消息,Message使用完后系统会调用recycle回笼,假使自个儿new比较多Message,每一次使用完后系统归入缓存池,会占用超级多内部存储器的,如下所示:

图片 20

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    public void recycle() {
        clearForRecycle();

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

图片 21

Message内部通过next成员贯彻了二个链表,那样sPool就了为了二个Messages的缓存链表。

音讯对象得到到了怎么发送呢,咱们都精晓是透过Handler的post、sendMessage等方式,其实这几个艺术最后都是调用的同三个艺术sendMessageAtTime:

图片 22

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    } 

图片 23

sendMessageAtTime获取到音信队列然后调用enqueueMessage方法,音讯队列mQueue是从与Handler关联的Looper得到的。

图片 24

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

图片 25

enqueueMessage将message的target设置为眼下的handler,然后调用MessageQueue的enqueueMessage,在调用queue.enqueueMessage以前判别了mAsynchronous,从名字看是异步音信的意味,要精通Asynchronous的作用,供给先理解二个概念Barrier。

音讯循环

         +-------+     +------------+   +------------------+   +--------------+                        
          |Handler|     |MessageQueue|   |NativeMessageQueue|   |Looper(Native)|                        
          +--+----+     +-----+------+   +---------+--------+   +-------+------+                        
             |                |                    |                    |                               
             |                |                    |                    |                               
sendMessage()|                |                    |                    |                               
+----------> |                |                    |                    |                               
             |                |                    |                    |                               
             |enqueueMessage()|                    |                    |                               
             +--------------> |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |  nativeWake()      |                    |                               
             |                |    wake()          |                    |                               
             |                +------------------> |                    |                               
             |                |                    |                    |                               
             |                |                    |    wake()          |                               
             |                |                    +------------------> |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |write(mWakeWritePipeFd, "W", 1)
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             +                +                    +                    +

第一通过调用Looper的loop方法开头音信监听。loop方法里会调用MessageQueue的next方法。next方法会梗塞线程直到有新闻赶到停止。

next方法通过调用nativePollOnce方法来监听事件。next方法内部逻辑如下所示(简化卡塔尔(قطر‎:

  1. a. 进入死循环,以参数timout=0调用nativePollOnce方法。
  2. b.
    借使音信队列中有新闻,nativePollOnce方法会将音讯保存在mMessage成员中。nativePollOnce方法重返后马上检查mMessage成员是否为空。
  3. c.
    如若mMessage不为空,那么检查它内定的运行时刻。即使比当下光阴要前,那么立刻赶回这一个mMessage,不然设置timeout为两个之差,步向下叁遍巡回。
  4. d.
    要是mMessage为空,那么设置timeout为-1,即下一次循环nativePollOnce永远窒碍。

nativePollOnce方法内部选择epoll机制在早先建构的管道上等候数据写入。选拔到多少后立刻读取并重回结果。

Barrier与Asynchronous Message

Barrier是哪些看头吧,从名字看是一个拦截器,在这里个拦截器后边的消息都不时不可能施行,直到这么些拦截器被移除了,MessageQueue有一个函数叫enqueueSyncBarier能够加上一个巴里r。

图片 26

    int enqueueSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

图片 27

在enqueueSyncBarrier中,obtain了三个Message,并设置msg.arg1=token,token仅是多个每一次调用enqueueSyncBarrier时自增的int值,指标是历次调用enqueueSyncBarrier时重返唯生机勃勃的多少个token,这几个Message相像供给安装举行时间,然后插入到新闻队列,特殊的是那几个Message没有安装target,即msg.target为null。

跻身音讯循环后会不停地从MessageQueue中取音信试行,调用的是MessageQueue的next函数,此中有这么生机勃勃段:

图片 28

Message msg = mMessages;
if (msg != null && msg.target == null) {
    // Stalled by a barrier.  Find the next asynchronous message in the queue.
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
}

图片 29

借使队列底部的音讯的target为null就意味着它是个巴里r,因为唯有两种方法往mMessages中增添音讯,黄金年代种是enqueueMessage,另生龙活虎种是enqueueBarrier,而enqueueMessage中假使mst.target为null是直接抛至极的,后边会看见。

所谓的异步消息其实正是那般的,我们得以通过enqueueBarrier往新闻队列中插入多少个Barrier,那么队列中试行时间在这里个Barrier现在的联合音信都会被那么些Barrier拦截住不恐怕施行,直到大家调用removeBarrier移除了这些Barrier,而异步新闻则没有影响,音讯暗中同意就是风华正茂道音信,除非大家调用了Message的setAsynchronous,那么些艺术是掩瞒的。独有在开始化Handler时因此参数钦命往那些Handler发送的音讯都是异步的,那样在Handler的enqueueMessage中就能够调用Message的setAsynchronous设置消息是异步的,从上面Handler.enqueueMessage的代码中得以看看。

 所谓异步消息,其实只有一个作用,便是在装置Barrier时还能够不受Barrier的震慑被不荒谬管理,若无安说大话arrier,异步音讯就与协助举办信息还未分化,能够通过removeSyncBarrier移除Barrier:

图片 30

void removeSyncBarrier(int token) {
    // Remove a sync barrier token from the queue.
    // If the queue is no longer stalled by a barrier then wake it.
    final boolean needWake;
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        if (prev != null) {
            prev.next = p.next;
            needWake = false;
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycle();
    }
    if (needWake) {
        nativeWake(mPtr);
    }
}

图片 31

参数token就是enqueueSyncBarrier的重临值,若无调用钦命的token不设有是会抛非凡的。

新闻发送

            +------+       +-------+                                                                   
             |Looper|       |Handler|                                                                   
             +--+---+       +---+---+                                                                   
                |               |                                                                       
                |               |                                                                       
    loop()      |               |                                                                       
    [after next()]              |                                                                       
    +---------> |               |                                                                       
                |               |                                                                       
                |dispatchMessage()                                                                      
                +-------------> |                                                                       
                |               |                                                                       
                |               |                                                                       
                |               | handleMessage()                                                       
                |               +-------+                                                               
                |               |       |                                                               
                |               |       |                                                               
                |               | <-----+                                                               
                |               |   (callback or subclass)                                              
                |               |                                                                       
                +               +

音信发送进度首要由Handler对象来驱动。

  1. Handler对象在成立时会保存当前线程的looper和MessageQueue,假若传入Callback的话也会保存起来。
  2. 客户调用handler对象的sendMessage方法,传入msg对象。handler通过调用MessageQueue的enqueueMessage方法将新闻压入MessageQueue。
  3. enqueueMessage方法会将盛传的新闻对象依照触发时间(when)插入到message
    queue中。然后推断是还是不是要提示等待中的队列。
    a.
    如果插在队列中间。表明该新闻没有须求及时管理,无需由那么些音讯来提醒队列。
    b.
    如若插在队列底部(大概when=0),则声明要立时管理这么些音讯。假如当前队列正在拥塞,则须求指示它举办拍卖。
  4. 后生可畏经必要提示队列,则通过nativeWake方法,往前边提到的管道中写入二个”W”字符,令nativePollOnce方法重临。

enqueueMessage

接下去看一下是怎么MessageQueue的enqueueMessage。

图片 32

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

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

            msg.when = when;
            Message p = mMessages;
            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;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

图片 33

在乎上边代码浅青的局地,当msg.target为null时是直接抛格外的。

在enqueueMessage中第大器晚成剖断,假若当前的新闻队列为空,恐怕新扩充长的音信的进行时间when是0,大概新加上的音讯的实施时间比音信队列头的音信的实践时间还早,就把新闻增多到音讯队列头(音信队列按期间排序),不然将在找到确切的职务将近些日子信息增加到新闻队列。

消息管理

+------+ +-------+  |Looper|       |Handler| +--+---+ +---+---+  | | | | loop() | | [after next()] | +---------> | |  | | |dispatchMessage() +-------------> |  | | | | | | handleMessage() | +-------+  | | | | | | | | <-----+  | | (callback or subclass) | | + + 

Looper对象的loop方法里面包车型大巴queue.next方法假设回到了message,那么handler的dispatchMessage会被调用。

a.
倘使新建Handler的时候传出了callback实例,那么callback的handleMessage方法会被调用。
b.
假设是由此post方法向handler传入runnable对象的,那么runnable对象的run方法会被调用。
c. 别的情状下,handler方法的handleMessage会被调用。

Native发送音信

消息模型不只是Java层用的,Native层也足以用,前边也看出了新闻队列开头化时也还要初始化了Native层的Looper与NativeMessageQueue,所以Native层应该也是足以发送音讯的。与Java层差异的是,Native层是透过Looper发新闻的,同样具有的出殡和安葬方法最后是调用sendMessageAtTime:

图片 34

void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
        const Message& message) {
#if DEBUG_CALLBACKS
    ALOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d",
            this, uptime, handler.get(), message.what);
#endif

    size_t i = 0;
    { // acquire lock
        AutoMutex _l(mLock);

        size_t messageCount = mMessageEnvelopes.size();
        while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
            i += 1;
        }

        MessageEnvelope messageEnvelope(uptime, handler, message);
        mMessageEnvelopes.insertAt(messageEnvelope, i, 1);

        // Optimization: If the Looper is currently sending a message, then we can skip
        // the call to wake() because the next thing the Looper will do after processing
        // messages is to decide when the next wakeup time should be.  In fact, it does
        // not even matter whether this code is running on the Looper thread.
        if (mSendingMessage) {
            return;
        }
    } // release lock

    // Wake the poll loop only when we enqueue a new message at the head.
    if (i == 0) {
        wake();
    }
}

图片 35

 Native
Message只有二个int型的what字段用来分别分化的讯息,sendMessageAtTime内定了Message,Message要试行的时光when,与拍卖这么些消息的Handler:MessageHandler,然后用MessageEnvelope封装了time,
MessageHandler与Message,Native层发的音信都保存到了mMessageEnvelopes中,mMessageEnvelopes是三个Vector<MessageEnvelope>。Native层音讯无差异于是准期间排序,与Java层的新闻分别保存在五个种类里。

音讯循环

音信队列起头化好了,也清楚怎么发新闻了,上边便是怎么管理音讯了,看Handler.loop函数:

图片 36

    public static void loop() {
        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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.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(TAG, "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();
        }
    }

图片 37

loop每一次从MessageQueue抽取多个Message,调用msg.target.dispatchMessage(msgState of Qatar,target正是出殡和下葬message时跟message关联的handler,那样就调用到了听得多了就能说的清楚的dispatchMessage,Message被拍卖后会被recycle。当queue.next重返null时会退出消息循环,接下去就看一下MessageQueue.next是怎么收取新闻的,又会在曾几何时回来null。

图片 38

final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                if (mQuiting) {
                    return null;
                }

                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                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.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

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

图片 39

MessageQueue.next首先会调用nativePollOnce,然后风流倜傥旦mQuiting为true就赶回null,Looper就能够脱离消息循环。

接下去取音信队列尾部的新闻,若是底部音讯是Barrier(target==null)就今后遍历找到第一个异步音讯,接下去检查测验获取到的音信(音信队列底部的音讯依然第三个异步信息),假诺为null表示未有消息要施行,设置nextPollTimeoutMillis

-1;不然检查实验这么些信息要施行的年月,若是到实行时间了就将以此消息markInUse并从新闻队列移除,然后从next重临到loop;不然设置nextPollTimeoutMillis
= (int卡塔尔国 Math.min(msg.when – now,
Integer.MAX_VALUE卡塔尔(قطر‎,即间距这段时间要进行的音信还必要多久,不论是当前音信队列未有新闻能够实践(设置了Barrier何况未有异步消息或音讯队列为空)依然队列底部的音信未到实践时间,都会履行前边的代码,看有未有设置IdleHandler,假诺有就运营IdleHandler,当IdleHandler被试行之后会设置nextPollTimeoutMillis
= 0。

率先看一下nativePollOnce,native方法,调用JNI,最终调到了Native
Looper::pollOnce,并从Java层传进去了nextPollTime米尔is,即Java层的音信队列中执行时间以来的音讯还要多长期到施行时间。

图片 40

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        while (mResponseIndex < mResponses.size()) {
            const Response& response = mResponses.itemAt(mResponseIndex++);
            int ident = response.request.ident;
            if (ident >= 0) {
                int fd = response.request.fd;
                int events = response.events;
                void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
                ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
                        "fd=%d, events=0x%x, data=%p",
                        this, ident, fd, events, data);
#endif
                if (outFd != NULL) *outFd = fd;
                if (outEvents != NULL) *outEvents = events;
                if (outData != NULL) *outData = data;
                return ident;
            }
        }

        if (result != 0) {
#if DEBUG_POLL_AND_WAKE
            ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
            if (outFd != NULL) *outFd = 0;
            if (outEvents != NULL) *outEvents = 0;
            if (outData != NULL) *outData = NULL;
            return result;
        }

        result = pollInner(timeoutMillis);
    }
}

图片 41

先不望起头的一大串代码,先看一下pollInner:

图片 42

int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif

    // Adjust the timeout based on when the next message is due.
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
            timeoutMillis = messageTimeoutMillis;
        }
#if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
                this, mNextMessageUptime - now, timeoutMillis);
#endif
    }

    // Poll.
    int result = ALOOPER_POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    // Acquire lock.
    mLock.lock();

    // Check for poll error.
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        ALOGW("Poll failed with an unexpected error, errno=%d", errno);
        result = ALOOPER_POLL_ERROR;
        goto Done;
    }

    // Check for poll timeout.
    if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ pollOnce - timeout", this);
#endif
        result = ALOOPER_POLL_TIMEOUT;
        goto Done;
    }

    // Handle all events.
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif

    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeReadPipeFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;

    // Invoke pending message callbacks.
    mNextMessageUptime = LLONG_MAX;
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
            // Remove the envelope from the list.
            // We keep a strong reference to the handler until the call to handleMessage
            // finishes.  Then we drop it so that the handler can be deleted *before*
            // we reacquire our lock.
            { // obtain handler
                sp<MessageHandler> handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                mLock.unlock();

#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
                ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
                        this, handler.get(), message.what);
#endif
                handler->handleMessage(message);
            } // release handler

            mLock.lock();
            mSendingMessage = false;
            result = ALOOPER_POLL_CALLBACK;
        } else {
            // The last message left at the head of the queue determines the next wakeup time.
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }

    // Release lock.
    mLock.unlock();

    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == ALOOPER_POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
            ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
                    this, response.request.callback.get(), fd, events, data);
#endif
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd);
            }
            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = ALOOPER_POLL_CALLBACK;
        }
    }
    return result;
}

图片 43

Java层的音信都保存在了Java层MessageQueue的成员mMessages中,Native层的音讯都封存在了Native
Looper的mMessageEnvelopes中,那就足以说有五个音信队列,而且皆以依期间排列的。timeOutMillis表示Java层下个要试行的音讯还要多长期实行,mNextMessageUpdate表示Native层下个要实施的音信还要多长期实践,假设timeOutMillis为0,epoll_wait不安装TimeOut直接回到;假诺为-1表达Java层无音信间接用Native的time
out;不然pollInner取这两个中的最小值作为timeOut调用epoll_wait。当epoll_wait再次回到时就只怕有以下三种情景:

  1. 一差二错再次来到。

  2. Time Out

  3. 健康再次回到,描述符上有事件时有发生。

大器晚成经是前二种景况向来goto DONE。

不然就印证FD上有事件时有产生了,假如是mWakeReadPipeFd的EPOLLIN事件就调用awoken,假使不是mWakeReadPipeFd,那就是经过addFD增添的fd,在addFD旅长要监听的fd及其events,callback,data封装成了Request对象,并以fd为键保存到了KeyedVector
mRequests中,所以在这里地就以fd为键拿到在addFD时涉嫌的Request,并连同events通过pushResonse到场mResonse队列(Vector),Resonse仅是对events与Request的包裹。如若是epoll_wait出错或timeout,就一贯不描述符上有事件,就无须施行这生机勃勃段代码,所以一直goto
DONE了。

图片 44

void Looper::pushResponse(int events, const Request& request) {
    Response response;
    response.events = events;
    response.request = request;
    mResponses.push(response);
}

图片 45

接下去步入DONE部分,从mMessageEnvelopes收取底部的Native音讯,借使到达了施行时间就调用它里面保存的MessageeHandler的handleMessage管理并从Native
音讯队列移除,设置result为ALOOPE翼虎_POLL_CALLBACK,不然总计mNextMessageUptime表示Native音信队列下三遍信息要奉行的年华。假设未到底部新闻的举办时间有希望是Java层新闻队列新闻的实行时间低于Native层消息队列底部音讯的实行时间,达到了Java层音信的试行时间epoll_wait
TimeOut重返了,或都由此addFd加多的描述符上有事件时有发生导致epoll_wait返回,或者epoll_wait是失误重返。Native音信是从未Barrier与Asynchronous的。

末尾,遍历mResponses(前边刚通过pushResponse存进去的),若是response.request.ident ==ALOOPEEnclave_POLL_CALLBACK,就调用注册的callback的handle伊芙nt(fd, events, data卡塔尔国实行管理,然后从mResonses队列中移除,这一次遍历完事后,mResponses中保存来来的就皆以ident>=0並且callback为NULL的了。在NativeMessageQueue开始化Looper时传入了mAllowNonCallbacks为false,所以此番拍卖完后mResponses一定为空。

接下去回去到pollOnce。pollOnce是二个for循环,pollInner中拍卖了全体response.request.ident==ALOOPE宝马7系_POLL_CALLBACK的Response,在第一次跻身for循环后若是mResponses不为空就足以找到ident>0的Response,将其ident作为重临值重回由调用pollOnce的函数自个儿管理,在这里地我们是在NativeMessageQueue中调用的Loope的pollOnce,没对再次来到值进行处理,而且mAllowNonCallbacks为false也就不容许步入这么些轮回。pollInner重返值不或者是0,也许说只或者是负数,所以pollOnce中的for循环只会执行四遍,在其次次就回去了。

Native
Looper能够独自使用,也会有二个prepare函数,这时候mAllowNonCallbakcs值只怕为true,pollOnce中对mResponses的拍卖就有意义了。

 wake与awoken

在Native
Looper的构造函数中,通过pipe张开了叁个管道,并用mWakeReadPipeFd与mWakeWritePipeFd分别保存了管道的读端与写端,然后用epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd,& eventItem卡塔尔(قطر‎监听了读端的EPOLLIN事件,在pollInner中通过epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis卡塔尔读取事件,那是在哪天往mWakeWritePipeFd写,又是在如哪一天候读的mWakeReadPipeFd呢?

在Looper.cpp中大家能够窥见如下三个函数:

图片 46

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    ssize_t nWrite;
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite != 1) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ awoken", this);
#endif

    char buffer[16];
    ssize_t nRead;
    do {
        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}

图片 47

wake函数向mWakeWritePipeFd写入了二个“W”字符,awoken从mWakeReadPipeFd读,往mWakeWritePipeFd写多少只是为着在pollInner中的epoll_wait能够监听到事件重回。在pollInner也得以看来如果是mWakeReadPipeFd的EPOLLIN事件只是调用了awoken消耗掉了写入的字符就现在甩卖了。

那什么样时候调用wake呢?这么些只要找到调用的地点剖判一下就行了,先看Looper.cpp,在sendMessageAtTime即发送Native
Message的时候,依据发送的Message的执行时间寻觅mMessageEnvelopes计算应该插入的任务,如若是在头顶插入,就调用wake唤醒epoll_wait,因为在步向pollInner时依照Java层新闻队列底部新闻的举行时间与Native层音讯队列尾部音信的实施时间估测计算出了二个timeout,若是这些新新闻是在头顶插入,表达奉行时间起码在上述多个新闻中的叁个事前,所以理应升迁epoll_wait,epoll_wait再次回到后,检查Native新闻队列,看底部音信即刚插入的新闻是不是到实践时间了,到了就进行,不然就或者要求设置新的timeout。雷同在Java层的MessageQueue中,有一个函数nativeWake也黄金时代律能够因此JNI调用wake,调用nativeWake的空子与在Native调用wake的空子近似,在新闻队列尾部插入消息,还大概有大器晚成种情景就是,新闻队列底部是多少个Barrier,並且插入的消息是首先个异步音讯。

图片 48

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();//如果头部是Barrier并且新消息是异步消息则“有可能”需要唤醒
    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;
}

图片 49

在头顶插入音信不自然调用nativeWake,因为事情发生前恐怕正在实行IdleHandler,借使推行了IdleHandler,就在IdleHandler试行后把nextPollTimeoutMillis设置为0,下一次进来for循环就用0调用nativePollOnce,无需wake,独有在尚未音讯能够施行(音信队列为空或没到实行时间)並且未有安装IdleHandler时mBlocked才会为true。

假设Java层的消息队列被Barrier
Block住了还要当前陈设的是叁个异步音讯有一点都不小希望须要提示Looper,因为异步新闻能够在Barrier下试行,不过那一个异步新闻必定借使实施时间最初的异步消息。

脱离Looper也须要wake,removeSyncBarrier时也可能须求。

发表评论

电子邮件地址不会被公开。 必填项已用*标注