android MessageQueue入门

接触安卓几年了。可是感受一直不是很明白,东西太多了。反过来讲就是本身太菜了。不少东西其实都是模凌两可,不熟悉,不少知识点都是知道一点,最多你们都这样用。没问题,事件长了也一直这样用的。
可是有个问题,安卓在不断的升级,可是本身的知识却升的很少。咋整? 想去全球顶级公司不? 想, 想去是好。可是得把软件开发的路子或者思想弄透彻了。得把实现机制弄懂了。这样估计就用机会了。java

咱们今天来谈谈Handler Looper Messagequeue的知识。这个东西是入门的东西,也是写安卓应用必备的知识点。可是我怎么感受还不透彻了。实在不透彻。无论别人怎么样,反正我记录下,最好能学会。android

多个handler绑定了一个Looper, 一个looper绑定了一个MessageQueue 臧春杰 marvell 这个都知道。 也知道java经过线程私有变量来保存looper信息。 也就是说一个线程最多有个一个Looper. 系统经过ThreadLocal方法来保存c++

线程私有变量。app

那下面一步就是冲消息队列取消息而后处理消息,若是没有消息就再等待。ide

咱们经过代码能够看到,
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
诡异了。这里有个jni方法,莫非消息队列是经过jni创建的?
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz //臧春杰 marvell) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}oop

nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
还真是,原来还真是
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
这里出现了咱们的主角,这里须要问一句, 线程在消息队列没有消 臧春杰 marvell 息的时候会阻塞,那如何阻塞呢? 是阻塞在java里的吗? 是阻塞这哪里的呢? 能够说就是阻塞在looper里,这里looper.cpp不要和Java层的Looper.java混淆了。post

这两个东西毫无关系,记得是毫无关系,陌生人,谁也不认识谁。
那c++这里是如何阻塞的呢? epoll多路监听阻塞,那阻塞的描述符是哪一个呢?Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), //臧春杰 marvell
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK);

mWakeEventFd = eventfd(0, EFD_NONBLOCK);就这个,套路啊。慢慢的全是套路,之前不是用pipe的吗?时代在进步,社会在发展,用了eventfd了。

那这就明白了,looper在epoll_wait等待唤醒。那紧接着问题来了,咱们常常使用好比5秒后唤醒,那这5秒是如何作到呢?系统如何计时5秒呢?一样也是epoll_wait参 臧春杰 marvell数有个timeout用来计时,
epoll_wait返回值须要说明下。
When successful, epoll_wait() returns the number of file descriptors ready for the requested I/O, or zero if no file
descriptor became ready during the requested timeout milliseconds. When an error occurs, epoll_wait() returns -1 and
errno is set appropriately.
这就是问题的关键。

其实在c++层面,也能够处理消息,也能够监听文件描述符。
int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
const Message& message); 臧春杰 marvell
因此咱们能够看到,不少在native service中,都使用了这些功能。

那既然在c++层面在监听,那如何唤醒呢? 很简单,就是让文件描述符可读,如何可读? 往文件描述符写东西

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

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);
}
}
}
经过这个东西,能够唤醒了。开始作消息处理。这就是为何消息先处理c++层面的消息,而后再处理java层面的消息。全部的消息处理都根据消息的触发时间来计算的。看当前时间和消息须要触发时间比较。this

经过这两个时间来计算timeout时间,这就要求消息队列是排序的,按照触发时间排序。线程

消息队列补充知识点:

咱们据说过桩子臧春杰 marvell的概念。postSyncBarrier,removeSyncBarrier 这个是干什么用呢? 桩子的做用是 桩子后面的消息系统不会处理,直到桩子被拿掉,这是干什么呢?

想一想这个场景,咱们在主线程干事情,往另外一个线程发消息,可是咱们得主线程干完,其余线程才能处理消息,那如何保证这个呢? 这就须要桩子,否则cpu 臧春杰 marvell 调度时候,咱们不能保证这个消息在何时处理。说不定,

sendMessage后,它立马就运行了。

当消息队列没有消息时候,咱们能够作点其余事情,
addIdleHandler

我通常用他来作垃圾回收。

相关文章
相关标签/搜索