Android6.0 消息机制原理解析

消息都是存放在一个消息队列中去,而消息循环线程就是围绕这个消息队列进入一个无限循环的,直到线程退出。如果队列中有消息,消息循环线程就会把它取出来,并分发给相应的Handler进行处理;如果队列中没有消息,消息循环线程就会进入空闲等待状态,等待下一个消息的到来。在编写Android应用程序时,当程序执行的任务比较繁重时,为了不阻塞UI主线程而导致ANR的发生,我们通常的做法的创建一个子线程来完成特定的任务。在创建子线程时,有两种选择,一种通过创建Thread对象来创建一个无消息循环的子线程;还有一种就是创建一个带有消息循环的子线程,而创建带有消息循环的子线程由于两种实现方法,一种是直接利用Android给我们封装好的HandlerThread类来直接生成一个带有消息循环的线程对象,另一种方法是在实现线程的run()方法内使用以下方式启动一个消息循环:

一、消息机制使用

通常消息都是有一个消息线程和一个Handler组成,下面我们看PowerManagerService中的一个消息Handler:

 mHandlerThread = new ServiceThread(TAG,
        Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
    mHandlerThread.start();
    mHandler = new PowerManagerHandler(mHandlerThread.getLooper());

这里的ServiceThread就是一个HandlerThread,创建Handler的时候,必须把HandlerThread的looper传进去,否则就是默认当前线程的looper。

而每个handler,大致如下:

   private final class PowerManagerHandler extends Handler {
    public PowerManagerHandler(Looper looper) {
      super(looper, null, true /*async*/);
    }

    @Override
    public void handleMessage(Message msg) {
      switch (msg.what) {
        case MSG_USER_ACTIVITY_TIMEOUT:
          handleUserActivityTimeout();
          break;
        case MSG_SANDMAN:
          handleSandman();
          break;
        case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT:
          handleScreenBrightnessBoostTimeout();
          break;
        case MSG_CHECK_WAKE_LOCK_ACQUIRE_TIMEOUT:
          checkWakeLockAquireTooLong();
          Message m = mHandler.obtainMessage(MSG_CHECK_WAKE_LOCK_ACQUIRE_TIMEOUT);
          m.setAsynchronous(true);
          mHandler.sendMessageDelayed(m, WAKE_LOCK_ACQUIRE_TOO_LONG_TIMEOUT);
          break;
      }
    }
  }

二、消息机制原理
那我们先来看下HandlerThread的主函数run函数:

 public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
      mLooper = Looper.myLooper();//赋值后notifyall,主要是getLooper函数返回的是mLooper
      notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
  }

再来看看Lopper的prepare函数,最后新建了一个Looper对象,并且放在线程的局部变量中。

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

Looper的构造函数中创建了MessageQueue

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

我们再来看下MessageQueue的构造函数,其中nativeInit是一个native方法,并且把返回值保存在mPtr显然是用long型变量保存的指针

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

native函数中主要创建了NativeMessageQueue对象,并且把指针变量返回了。

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

NativeMessageQueue构造函数就是获取mLooper,如果没有就是新建一个Looper

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

然后我们再看下Looper的构造函数,显示调用了eventfd创建了一个fd,eventfd它的主要是用于进程或者线程间的通信,我们可以看下这篇博客eventfd介绍

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

2.1 c层创建epoll

我们再来看下rebuildEpollLocked函数,创建了epoll,并且把mWakeEventFd加入epoll,而且把mRequests的fd也加入epoll

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

继续回到HandlerThread的run函数,我们继续分析Looper的loop函数

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

我们看看Looper的loop函数:

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;//得到Looper的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这个函数会阻塞,阻塞主要是epoll_wait
      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.recycleUnchecked();
    }
  }

MessageQueue类的next函数主要是调用了nativePollOnce函数,后面就是从消息队列中取出一个Message

Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    final long ptr = mPtr;//之前保留的指针
    if (ptr == 0) {
      return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
      if (nextPollTimeoutMillis != 0) {
        Binder.flushPendingCommands();
      }

      nativePollOnce(ptr, nextPollTimeoutMillis);

下面我们主要看下nativePollOnce这个native函数,把之前的指针强制转换成NativeMessageQueue,然后调用其pollOnce函数

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

2.2 c层epoll_wait阻塞

pollOnce函数,这个函数前面的while一般都没有只是处理了indent大于0的情况,这种情况一般没有,所以我们可以直接看pollInner函数

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

pollInner函数主要就是调用epoll_wait阻塞,并且java层会计算每次阻塞的时间传到c层,等待有mWakeEventFd或者之前addFd的fd有事件过来,才会epoll_wait返回。

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 %" PRId64 "ns, adjusted timeout: timeoutMillis=%d",
        this, mNextMessageUptime - now, timeoutMillis);
#endif
  }

  // Poll.
  int result = POLL_WAKE;
  mResponses.clear();//清空mResponses
  mResponseIndex = 0;

  // We are about to idle.
  mPolling = true;

  struct epoll_event eventItems[EPOLL_MAX_EVENTS];
  int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);//epoll_wait主要线程阻塞在这,这个阻塞的时间也是有java层传过来的

  // No longer idling.
  mPolling = false;

  // Acquire lock.
  mLock.lock();

  // Rebuild epoll set if needed.
  if (mEpollRebuildRequired) {
    mEpollRebuildRequired = false;
    rebuildEpollLocked();
    goto Done;
  }

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

  // Check for poll timeout.
  if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - timeout", this);
#endif
    result = 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 == mWakeEventFd) {//通知唤醒线程的事件
      if (epollEvents & EPOLLIN) {
        awoken();
      } else {
        ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
      }
    } else {
      ssize_t requestIndex = mRequests.indexOfKey(fd);//之前addFd的事件
      if (requestIndex >= 0) {
        int events = 0;
        if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
        if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
        if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
        if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
        pushResponse(events, mRequests.valueAt(requestIndex));//放在mResponses中
      } 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) {// 这块主要是c层的消息,java层的消息是自己管理的
    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 = 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++) {//这是之前addFd的事件的处理,主要是遍历mResponses,然后调用其回调
    Response& response = mResponses.editItemAt(i);
    if (response.request.ident == 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
      // Invoke the callback. Note that the file descriptor may be closed by
      // the callback (and potentially even reused) before the function returns so
      // we need to be a little careful when removing the file descriptor afterwards.
      int callbackResult = response.request.callback->handleEvent(fd, events, data);
      if (callbackResult == 0) {
        removeFd(fd, response.request.seq);
      }

      // 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 = POLL_CALLBACK;
    }
  }
  return result;
}

继续分析Looper的loop函数,可以增加自己的打印来调试代码,之前调用Message的target的dispatchMessage来分配消息

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

2.3 增加调试打印

我们先来看自己添加打印,可以通过Lopper的setMessageLogging函数来打印

public void setMessageLogging(@Nullable Printer printer) {
    mLogging = printer;
  }
Printer就是一个interface

public interface Printer {
  /**
   * Write a line of text to the output. There is no need to terminate
   * the given string with a newline.
   */
  void println(String x);
}

2.4 java层消息分发处理

再来看消息的分发,先是调用Handler的obtainMessage函数

 Message msg = mHandler.obtainMessage(MSG_CHECK_WAKE_LOCK_ACQUIRE_TIMEOUT);
 msg.setAsynchronous(true);
 mHandler.sendMessageDelayed(msg, WAKE_LOCK_ACQUIRE_TOO_LONG_TIMEOUT);

先看obtainMessage调用了Message的obtain函数

public final Message obtainMessage(int what)
  {
    return Message.obtain(this, what);
  }

Message的obtain函数就是新建一个Message,然后其target就是设置成其Handler

public static Message obtain(Handler h, int what) {
    Message m = obtain();//就是新建一个Message
    m.target = h;
    m.what = what;

    return m;
  }

我们再联系之前分发消息

msg.target.dispatchMessage(msg);最后就是调用Handler的dispatchMessage函数,最后在Handler中,最后会根据不同的情况对消息进行处理。

   public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
      handleCallback(msg);//这种就是用post形式发送,带Runnable的
    } else {
      if (mCallback != null) {//这种是handler传参的时候就是传入了mCallback回调了
        if (mCallback.handleMessage(msg)) {
          return;
        }
      }
      handleMessage(msg);//最后就是在自己实现的handleMessage处理
    }
  }

2.3 java层 消息发送

我们再看下java层的消息发送,主要也是调用Handler的sendMessage post之类函数,最终都会调用下面这个函数

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

我们再来看java层发送消息最终都会调用enqueueMessage函数

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

最终在enqueueMessage中,把消息加入消息队列,然后需要的话就调用c层的nativeWake函数

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

我们看下这个native方法,最后也是调用了Looper的wake函数

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

Looper类的wake,函数只是往mWakeEventfd中写了一些内容,这个fd只是通知而已,类似pipe,最后会把epoll_wait唤醒,线程就不阻塞了继续先发送c层消息,然后处理之前addFd的事件,然后处理java层的消息。

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

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

2.4 c层发送消息

在c层也是可以发送消息的,主要是调用Looper的sendMessageAtTime函数,参数有有一个handler是一个回调,我们把消息放在mMessageEnvelopes中。

 void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
    const Message& message) {
#if DEBUG_CALLBACKS
  ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", 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();
  }
}

当在pollOnce中,在epoll_wait之后,会遍历mMessageEnvelopes中的消息,然后调用其handler的handleMessage函数

   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 = POLL_CALLBACK;
    } else {
      // The last message left at the head of the queue determines the next wakeup time.
      mNextMessageUptime = messageEnvelope.uptime;
      break;
    }
  }

有一个Looper_test.cpp文件,里面介绍了很多Looper的使用方法,我们来看下

   sp<StubMessageHandler> handler = new StubMessageHandler();
  mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1));
StubMessageHandler继承MessageHandler就必须实现handleMessage方法

class StubMessageHandler : public MessageHandler {
public:
  Vector<Message> messages;

  virtual void handleMessage(const Message& message) {
    messages.push(message);
  }
};

我们再顺便看下Message和MessageHandler类

 struct Message {
  Message() : what(0) { }
  Message(int what) : what(what) { }

  /* The message type. (interpretation is left up to the handler) */
  int what;
};

/**
 * Interface for a Looper message handler.
 *
 * The Looper holds a strong reference to the message handler whenever it has
 * a message to deliver to it. Make sure to call Looper::removeMessages
 * to remove any pending messages destined for the handler so that the handler
 * can be destroyed.
 */
class MessageHandler : public virtual RefBase {
protected:
  virtual ~MessageHandler() { }

public:
  /**
   * Handles a message.
   */
  virtual void handleMessage(const Message& message) = 0;
};

2.5 c层addFd

我们也可以在Looper.cpp的addFd中增加fd放入线程epoll中,当fd有数据来我们也可以处理相应的数据,下面我们先来看下addFd函数,我们注意其中有一个callBack回调

 int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
  return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
}

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

  { // acquire lock
    AutoMutex _l(mLock);

    Request request;
    request.fd = fd;
    request.ident = ident;
    request.events = events;
    request.seq = mNextRequestSeq++;
    request.callback = callback;
    request.data = data;
    if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1

    struct epoll_event eventItem;
    request.initEventItem(&eventItem);

    ssize_t requestIndex = mRequests.indexOfKey(fd);
    if (requestIndex < 0) {
      int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);//加入epoll
      if (epollResult < 0) {
        ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
        return -1;
      }
      mRequests.add(fd, request);//放入mRequests中
    } else {
      int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);//更新
      if (epollResult < 0) {
        if (errno == ENOENT) {
          // Tolerate ENOENT because it means that an older file descriptor was
          // closed before its callback was unregistered and meanwhile a new
          // file descriptor with the same number has been created and is now
          // being registered for the first time. This error may occur naturally
          // when a callback has the side-effect of closing the file descriptor
          // before returning and unregistering itself. Callback sequence number
          // checks further ensure that the race is benign.
          //
          // Unfortunately due to kernel limitations we need to rebuild the epoll
          // set from scratch because it may contain an old file handle that we are
          // now unable to remove since its file descriptor is no longer valid.
          // No such problem would have occurred if we were using the poll system
          // call instead, but that approach carries others disadvantages.
#if DEBUG_CALLBACKS
          ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
              "being recycled, falling back on EPOLL_CTL_ADD, errno=%d",
              this, errno);
#endif
          epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
          if (epollResult < 0) {
            ALOGE("Error modifying or adding epoll events for fd %d, errno=%d",
                fd, errno);
            return -1;
          }
          scheduleEpollRebuildLocked();
        } else {
          ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
          return -1;
        }
      }
      mRequests.replaceValueAt(requestIndex, request);
    }
  } // release lock
  return 1;
}

在pollOnce函数中,我们先寻找mRequests中匹配的fd,然后在pushResponse中新建一个Response,然后把Response和Request匹配起来。

     } else {
      ssize_t requestIndex = mRequests.indexOfKey(fd);
      if (requestIndex >= 0) {
        int events = 0;
        if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
        if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
        if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
        if (epollEvents & EPOLLHUP) events |= 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);
      }
    }

下面我们就会遍历mResponses中的Response,然后调用其request中的回调

   for (size_t i = 0; i < mResponses.size(); i++) {
    Response& response = mResponses.editItemAt(i);
    if (response.request.ident == 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
      // Invoke the callback. Note that the file descriptor may be closed by
      // the callback (and potentially even reused) before the function returns so
      // we need to be a little careful when removing the file descriptor afterwards.
      int callbackResult = response.request.callback->handleEvent(fd, events, data);
      if (callbackResult == 0) {
        removeFd(fd, response.request.seq);
      }

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

同样我们再来看看Looper_test.cpp是如何使用的?

   Pipe pipe;
  StubCallbackHandler handler(true);

  handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);

我们看下handler的setCallback函数

class CallbackHandler {
public:
  void setCallback(const sp<Looper>& looper, int fd, int events) {
    looper->addFd(fd, 0, events, staticHandler, this);//就是调用了looper的addFd函数,并且回调
  }

protected:
  virtual ~CallbackHandler() { }

  virtual int handler(int fd, int events) = 0;

private:
  static int staticHandler(int fd, int events, void* data) {//这个就是回调函数
    return static_cast<CallbackHandler*>(data)->handler(fd, events);
  }
};

class StubCallbackHandler : public CallbackHandler {
public:
  int nextResult;
  int callbackCount;

  int fd;
  int events;

  StubCallbackHandler(int nextResult) : nextResult(nextResult),
      callbackCount(0), fd(-1), events(-1) {
  }

protected:
  virtual int handler(int fd, int events) {//这个是通过回调函数再调到这里的
    callbackCount += 1;
    this->fd = fd;
    this->events = events;
    return nextResult;
  }
};

我们结合Looper的addFd一起来看,当callback是有的,我们新建一个SimpleLooperCallback

 int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
  return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
}

这里的Looper_callbackFunc是一个typedef
typedef int (*Looper_callbackFunc)(int fd, int events, void* data);

我们再来看SimpleLooperCallback

 class SimpleLooperCallback : public LooperCallback {
protected:
  virtual ~SimpleLooperCallback();

public:
  SimpleLooperCallback(Looper_callbackFunc callback);
  virtual int handleEvent(int fd, int events, void* data);

private:
  Looper_callbackFunc mCallback;
};SimpleLooperCallback::SimpleLooperCallback(Looper_callbackFunc callback) :
    mCallback(callback) {
}

SimpleLooperCallback::~SimpleLooperCallback() {
}

int SimpleLooperCallback::handleEvent(int fd, int events, void* data) {
  return mCallback(fd, events, data);
}

最后我们是调用callback->handleEvent(fd, events, data),而callback就是SimpleLooperCallback,这里的data,之前传进来的就是CallbackHandler 的this指针
 因此最后就是调用了staticHandler,而data->handler,就是this->handler,最后是虚函数就调用到了StubCallbackHandler 的handler函数中了。

当然我们也可以不用这么复杂,直接使用第二个addFd函数,当然callBack我们需要自己定义一个类来实现LooperCallBack类就行了,这样就简单多了。
 int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);

2.6 java层addFd

一直以为只能在c层的Looper中才能addFd,原来在java层也通过jni做了这个功能。

我们可以在MessageQueue中的addOnFileDescriptorEventListener来实现这个功能

   public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
      @OnFileDescriptorEventListener.Events int events,
      @NonNull OnFileDescriptorEventListener listener) {
    if (fd == null) {
      throw new IllegalArgumentException("fd must not be null");
    }
    if (listener == null) {
      throw new IllegalArgumentException("listener must not be null");
    }

    synchronized (this) {
      updateOnFileDescriptorEventListenerLocked(fd, events, listener);
    }
  }

我们再来看看OnFileDescriptorEventListener 这个回调

   public interface OnFileDescriptorEventListener {
    public static final int EVENT_INPUT = 1 << 0;
    public static final int EVENT_OUTPUT = 1 << 1;
    public static final int EVENT_ERROR = 1 << 2;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag=true, value={EVENT_INPUT, EVENT_OUTPUT, EVENT_ERROR})
    public @interface Events {}

    @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);
  }

接着调用了updateOnFileDescriptorEventListenerLocked函数

 private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
      OnFileDescriptorEventListener listener) {
    final int fdNum = fd.getInt$();

    int index = -1;
    FileDescriptorRecord record = null;
    if (mFileDescriptorRecords != null) {
      index = mFileDescriptorRecords.indexOfKey(fdNum);
      if (index >= 0) {
        record = mFileDescriptorRecords.valueAt(index);
        if (record != null && record.mEvents == events) {
          return;
        }
      }
    }

    if (events != 0) {
      events |= OnFileDescriptorEventListener.EVENT_ERROR;
      if (record == null) {
        if (mFileDescriptorRecords == null) {
          mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
        }
        record = new FileDescriptorRecord(fd, events, listener);//fd保存在FileDescriptorRecord对象
        mFileDescriptorRecords.put(fdNum, record);//mFileDescriptorRecords然后保存在
      } else {
        record.mListener = listener;
        record.mEvents = events;
        record.mSeq += 1;
      }
      nativeSetFileDescriptorEvents(mPtr, fdNum, events);//调用native函数
    } else if (record != null) {
      record.mEvents = 0;
      mFileDescriptorRecords.removeAt(index);
    }
  }

native最后调用了NativeMessageQueue的setFileDescriptorEvents函数

static void android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv* env, jclass clazz,
    jlong ptr, jint fd, jint events) {
  NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
  nativeMessageQueue->setFileDescriptorEvents(fd, events);
}

setFileDescriptorEvents函数,这个addFd就是调用的第二个addFd,因此我们可以肯定NativeMessageQueue继承了LooperCallback

 void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) {
  if (events) {
    int looperEvents = 0;
    if (events & CALLBACK_EVENT_INPUT) {
      looperEvents |= Looper::EVENT_INPUT;
    }
    if (events & CALLBACK_EVENT_OUTPUT) {
      looperEvents |= Looper::EVENT_OUTPUT;
    }
    mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents, this,
        reinterpret_cast<void*>(events));
  } else {
    mLooper->removeFd(fd);
  }
}

果然是,需要实现handleEvent函数

 class NativeMessageQueue : public MessageQueue, public LooperCallback {
public:
  NativeMessageQueue();
  virtual ~NativeMessageQueue();

  virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj);

  void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis);
  void wake();
  void setFileDescriptorEvents(int fd, int events);

  virtual int handleEvent(int fd, int events, void* data);

handleEvent就是在looper中epoll_wait之后,当我们增加的fd有数据就会调用这个函数

 int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) {
  int events = 0;
  if (looperEvents & Looper::EVENT_INPUT) {
    events |= CALLBACK_EVENT_INPUT;
  }
  if (looperEvents & Looper::EVENT_OUTPUT) {
    events |= CALLBACK_EVENT_OUTPUT;
  }
  if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP | Looper::EVENT_INVALID)) {
    events |= CALLBACK_EVENT_ERROR;
  }
  int oldWatchedEvents = reinterpret_cast<intptr_t>(data);
  int newWatchedEvents = mPollEnv->CallIntMethod(mPollObj,
      gMessageQueueClassInfo.dispatchEvents, fd, events); //调用回调
  if (!newWatchedEvents) {
    return 0; // unregister the fd
  }
  if (newWatchedEvents != oldWatchedEvents) {
    setFileDescriptorEvents(fd, newWatchedEvents);
  }
  return 1;
}

最后在java的MessageQueue中的dispatchEvents就是在jni层反调过来的,然后调用之前注册的回调函数

// Called from native code.
  private int dispatchEvents(int fd, int events) {
    // Get the file descriptor record and any state that might change.
    final FileDescriptorRecord record;
    final int oldWatchedEvents;
    final OnFileDescriptorEventListener listener;
    final int seq;
    synchronized (this) {
      record = mFileDescriptorRecords.get(fd);//通过fd得到FileDescriptorRecord
      if (record == null) {
        return 0; // spurious, no listener registered
      }

      oldWatchedEvents = record.mEvents;
      events &= oldWatchedEvents; // filter events based on current watched set
      if (events == 0) {
        return oldWatchedEvents; // spurious, watched events changed
      }

      listener = record.mListener;
      seq = record.mSeq;
    }

    // Invoke the listener outside of the lock.
    int newWatchedEvents = listener.onFileDescriptorEvents(//listener回调
        record.mDescriptor, events);
    if (newWatchedEvents != 0) {
      newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
    }

    // Update the file descriptor record if the listener changed the set of
    // events to watch and the listener itself hasn't been updated since.
    if (newWatchedEvents != oldWatchedEvents) {
      synchronized (this) {
        int index = mFileDescriptorRecords.indexOfKey(fd);
        if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
            && record.mSeq == seq) {
          record.mEvents = newWatchedEvents;
          if (newWatchedEvents == 0) {
            mFileDescriptorRecords.removeAt(index);
          }
        }
      }
    }

    // Return the new set of events to watch for native code to take care of.
    return newWatchedEvents;
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android的消息机制

    一.简介 Android的消息机制主要是指Handler的运行机制,那么什么是Handler的运行机制那?通俗的来讲就是,使用Handler将子线程的Message放入主线程的Messagequeue中,在主线程使用. 二.学习内容 学习Android的消息机制,我们需要先了解如下内容. 消息的表示:Message 消息队列:MessageQueue 消息循环,用于循环取出消息进行处理:Looper 消息处理,消息循环从消息队列中取出消息后要对消息进行处理:Handler 平常我们接触的大多是H

  • Android中Handler消息传递机制

    Handler 是用来干什么的? 1)执行计划任务,可以在预定的时间执行某些任务,可以模拟定时器 2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以在你的子线程中拿到父线程中创建的Handler 对象,就可以通过该对象向父线程的消息队列发送消息了.由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面. 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发

  • Android 消息机制问题总结

    Android的消息机制几乎是面试必问的话题,当然也并不是因为面试,而去学习,更重要的是它在Android的开发中是必不可少的,占着举足轻重的地位,所以弄懂它是很有必要的.下面就来说说最基本的东西. Looper 作用: 关联起Thread 循环取出消息 1.Looper是否可以直接实例化? Looper构造方法是私有的,其中做了两件事 创建一个MessageQueue 得到与之对应的Thread private Looper(boolean quitAllowed) { mQueue = ne

  • 深入浅析Android消息机制

    在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易.更好地架构系统,避免一些低级的错误. 每一个Android应用在启动的时候都会创建一个线程,这个线程被称为主线程或者UI线程,Android应用的所有操作默认都会运行在这个线程中. 但是当我们想要进行数据请求,图片下载,或者其他耗时操作时,是不可能在这个UI线程做的,因为Android在3.0以后的版本已经禁止了这件事情,直接抛出一个异常.所以我们需要一个子线程来处理那些

  • Android消息循环机制源码深入理解

    Android消息循环机制源码 前言: 搞Android的不懂Handler消息循环机制,都不好意思说自己是Android工程师.面试的时候一般也都会问这个知识点,但是我相信大多数码农肯定是没有看过相关源码的,顶多也就是网上搜搜,看看别人的文章介绍.学姐不想把那个万能的关系图拿出来讨论. 近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子. andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图片,当它下

  • 学习Android Handler消息传递机制

    Android只允许UI线程修改Activity里的UI组件.当Android程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件.屏幕绘图事件,并把相关的事件分发到对应的组件进行处理.所以,主线程通常又被称为UI线程. Android只允许UI线程修改Activity里的UI组件,这样会导致新启动的线程无法动态改变界面组件的属性值.但在实际的Android程序开发中,尤其是涉及动画的游戏开发中,需要让新启动的线程周

  • Android 消息机制以及handler的内存泄露

    Handler 每个初学Android开发的都绕不开Handler这个"坎",为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部分人都是知其然却不知其所以然.今天看到Handler.post这个方法之后决定再去翻翻源代码梳理一下Handler的实现机制. 异步更新UI 先来一个必背口诀"主线程不做耗时操作,子线程不更新UI",这个规定应该是初学必知的,那要怎么来解决口诀里的问题呢,这时候Handler就出现在我们面前了(AsyncTask也行,不过本质

  • Android消息机制Handler的工作过程详解

    综述 在Android系统中,出于对性能优化的考虑,对于Android的UI操作并不是线程安全的.也就是说若是有多个线程来操作UI组件,就会有可能导致线程安全问题.所以在Android中规定只能在UI线程中对UI进行操作.这个UI线程是在应用第一次启动时开启的,也称之为主线程(Main Thread),该线程专门用来操作UI组件,在这个UI线程中我们不能进行耗时操作,否则就会出现ANR(Application Not Responding)现象.如果我们在子线程中去操作UI,那么程序就回给我们抛

  • Android中利用App实现消息推送机制的代码

    1.消息推送机制 服务器器端需要变被动为主动,通知客户一些开发商认为重要的信息,无论应用程序是否正在运行或者关闭. 我想到了一句话:don't call me,i will call you! qq今天在右下角弹出了一个对话框:"奥巴马宣布本拉登挂了...",正是如此. 自作聪明,就会带点小聪明,有人喜欢就有人讨厌. 2.独立进程 无论程序是否正在运行,我们都要能通知到客户,我们需要一个独立进程的后台服务. 我们需要一个独立进程的后台服务. 在androidmanifest.xml中注

  • Android6.0 消息机制原理解析

    消息都是存放在一个消息队列中去,而消息循环线程就是围绕这个消息队列进入一个无限循环的,直到线程退出.如果队列中有消息,消息循环线程就会把它取出来,并分发给相应的Handler进行处理:如果队列中没有消息,消息循环线程就会进入空闲等待状态,等待下一个消息的到来.在编写Android应用程序时,当程序执行的任务比较繁重时,为了不阻塞UI主线程而导致ANR的发生,我们通常的做法的创建一个子线程来完成特定的任务.在创建子线程时,有两种选择,一种通过创建Thread对象来创建一个无消息循环的子线程:还有一

  • Java方法参数传递机制原理解析

    这篇文章主要介绍了Java方法参数传递机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Java方法中如果声明了形参,在调用方法时就必须给这些形参指定参数值,实际传进去的这个值就叫做实参. 这就涉及到Java中的参数传递机制,值传递. 基本数据类型 基本数据类型,值传递的体现是数值的传递. public class TransferTempTest { public static void main(String[] args) {

  • SpringBoot服务监控机制原理解析(面试官常问)

    前言 任何一个服务如果没有监控,那就是两眼一抹黑,无法知道当前服务的运行情况,也就无法对可能出现的异常状况进行很好的处理,所以对任意一个服务来说,监控都是必不可少的. 就目前而言,大部分微服务应用都是基于 SpringBoot 来构建,所以了解 SpringBoot 的监控特性是非常有必要的,而 SpringBoot 也提供了一些特性来帮助我们监控应用. 本文基于 SpringBoot 2.3.1.RELEASE 版本演示. SpringBoot 监控 SpringBoot 中的监控可以分为 H

  • C++实现简单插件机制原理解析

    在我做的第一个页游项目中,服务器使用了插件的机制,但是当时的插件都是用C#写,而且如何实现的也不是很清楚.之后的几个页游项目都是自己一个人包揽服务器部分,所以一直没有写插件的需求.下一个页游项目服务器这边需要多人合作,因此我想把其他模块都独立的做成插件的模式,目前也是在探索阶段.通过网上资料查找以及自己的整理,实现了一个简单版本的插件机制.实现代码如下: 文件Object.hpp中实现了所有插件类的基类,所有插件都要继承该类. #ifndef __OBJECT_HPP__ #define __O

  • zookeeper的watch机制原理解析

    目录 一.Watch机制介绍 二.zkCli客户端使⽤watch 2.1.get -w命令详解 2.2.ls -w命令详解 2.3.ls -R -w命令详解 三.curator客户端使⽤watch 一.Watch机制介绍 我们可以把 Watch 理解成是注册在特定 Znode 上的触发器.当这个 Znode 发⽣改变,也就是调⽤了 create , delete , setData ⽅法的时候,将会触发 Znode 上注册的对应事件,请求 Watch 的客户端会接收到异步通知. 具体交互过程如下

  • 微信公众号被动消息回复原理解析

    背景:某分厂需要实时查询工件堆放的位置,要求快速便捷,因此设计了采用微信公众号被动回复信息的方案. 技术实现:开发者服务器--基于Angular2框架的已发布网站,编程语言为Python,后台存储数据库为Mysql: 微信服务器--微信公众号,此业务只是处理微信客户端发送的文本信息,且不使用公众号的其他功能,因此不需要认证公众号: 微信客户端--关注公众号的微信使用者,即粉丝. 当粉丝给公众号发送特定的消息时,微信公众号自动回复相应内容,而其背后的实现原理可由下图所示: 由上图可知,粉丝(微信客

  • java的package和import机制原理解析

    这篇文章主要介绍了java的package和import机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在说package.import机制前我们先来了解下java的CLASSPATH. CLASSPATH顾名思义就是class的路径,当我们在系统中运行某个java程序时,它就会告诉系统在这些地方寻找这个class文件 CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar; 这是

  • SpringCloud Eureka自我保护机制原理解析

    这篇文章主要介绍了SpringCloud Eureka自我保护机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1. 自我保护机制演示 eureka在频繁修改微服务名称的时候,可以会出现如下现象: 2. 什么是自我保护模式? 默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒).但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信

  • Java和Dubbo的SPI机制原理解析

    SPI: 简单理解就是,你一个接口有多种实现,然后在代码运行时候,具体选用那个实现,这时候我们就可以通过一些特定的方式来告诉程序寻用那个实现类,这就是SPI. JAVA的SPI 全称为 Service Provider Interface,是一种服务发现机制.它是约定在 Classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,然后文件里面记录的是此 jar 包提供的具体实现类的全限定名. 这样当我们引用了某个 jar 包的时候就可以去找这个 jar 包

  • 深入剖析Android消息机制原理

    在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易.更好地架构系统,避免一些低级的错误.在学习Android中消息机制之前,我们先了解与消息有关的几个类: 1.Message 消息对象,顾名思义就是记录消息信息的类.这个类有几个比较重要的字段: a.arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在Service中,我们可以用来存放Service的ID. b.obj:该字段是Object类型,我们可以

随机推荐