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

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

相關文章
相關標籤/搜索