Android 消息传递机制
线程间消息传递机制
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Handler.java
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/MessageQueue.java
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Looper.java
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Message.java

1.消息怎么发送的?
我们都知道当调用Handler发送消息的时候,不管是调用
sendMessage,sendEmptyMessage,sendMessageDelayed还是其他发送一系列方法。最终都会调用sendMessageDelayed(Message msg, long delayMillis)方法。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
   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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
消息的加入
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 {
                //判断唤醒条件,当前当前消息队列头部消息是屏障消息,且当前插入的消息为异步消息
                //且当前消息队列处于无消息可处理的状态
                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;
                prev.next = msg;
            }
            //调用nativeWake,以触发nativePollOnce函数结束等待
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
这里大家肯定注意到了nativeWake()方法,这里先不对该方法进行详细的描述,下文会对其解释及介绍。 其实通过代码大家就应该发现了,在将消息加入到消息队列中时,已经将消息按照等待时间进行了排序。排序分为两种情况(这里图中的message.when是与当前的系统的时间差):
- 第一种:如果队列中没有消息,或者当前进入的消息比消息队列中头部的消息等待时间短,那么就放在消息队列的头部

第二种:反之,循环遍历消息队列,把当前进入的消息放入合适的位置(比较等待时间)

综上,我们了解了在我们使用Handler发送消息时,当消息进入到MessageQueue(消息队列)中时,已经按照等待时间进行了排序,且其头部对应的消息是Loop即将取出的消息
android_os_MessageQueue_nativeWake
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}
和之前一样,也是通过long类型的ptr获取NativeMessageQueue对象的指针,再调用wake方法
void NativeMessageQueue::wake() {
    mLooper->wake();
}
调用的也是Looper的方法:
void Looper::wake() {
    uint64_t inc = ;
    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: %s", strerror(errno));
        }
    }
}
重点是write(mWakeEventFd, &inc, sizeof(uint64_t)),写入了一个1,这个时候epoll就能监听到事件,也就被唤醒了
2.怎么样进行消息循环
我们都知道消息的取出,是通过Loooper.loop()方法,其中loop()方法内部会调用MessageQueue中的next()方法。
  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;
            }
      //...
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
              //...
            } finally {
              //...
            }
            //...
            msg.recycleUnchecked();
        }
    }        
从MessageQueue获取下一条消息
1.如果是null,退出
2.如果不为null,dispatchMessage
怎么处理消费的分发过程?
public void dispatchMessage(Message msg){
          //检查message的callback是否为null
          if(msg.calllback!=null){
              handleCallback(msg);
           }
            else{
                  if(mCallback!=null){
                            if(mCallback.handleMessage(mssg)){//主意如果这个返回值是true 将不会执行下面的handlerMessage
                                    return;
                              }
                   }
                    handleMessage(msg);
             }
}
首先,检查Message的callback是否为null,不为空就通过handleCallback来处理消息。message的callback是一个Runnbale对象,实际上就是Handler的post方法所传递的Runnable参数。
private static void handleCallback(Message message){
        message.callback.run();
}
其次,检查mCallback是否为空,不为null及调用mCallback的handleMessage方法来处理消息
public interface Callback{
      public boolean handleMessage(Message msg);
}
MessageQueue的next方法
Message next() {
        //...
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //执行native层消息机制层,
            //timeOutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。
            //如果值为0,则无需等待立即返回。该方法可能会阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                //获取系统开机到现在的时间,如果使用System.currentMillis()会有误差,
                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());
                }
                  msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                //...
        }
    }
Message next() {
        //如果退出消息消息循环,那么就直接退出
        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();
            }
            //执行native层消息机制层,
            //timeOutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。
            //如果值为0,则无需等待立即返回。该方法可能会阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                //获取系统开机到现在的时间,如果使用System.currentMillis()会有误差,
                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) {//判断当前消息时间,是不是比当前时间大,计算时间差
                        // 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 {
                        // 不需要等待时间或者等待时间已经到了,那么直接返回该消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //没有更多的消息了
                    nextPollTimeoutMillis = -1;
                }
                // Process the quit message now that all pending messages have been handled.
                //判断是否已经退出了
                if (mQuitting) {
                    dispose();
                    return null;
                }
                // 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.
                //获取空闲时处理任务的handler 用于发现线程何时阻塞等待更多消息的回调接口。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //如果空闲时处理任务的handler个数为0,继续让线程阻塞
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
                //判断当前空闲时处理任务的handler是否是为空
                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; // release the reference to the handler
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                //如果不保存空闲任务,执行完成后直接删除
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            // 重置空闲的handler个数,因为不需要重复执行
            pendingIdleHandlerCount = 0;
            //当执行完空闲的handler的时候,新的native消息可能会进入,所以唤醒Native消息机制层
            nextPollTimeoutMillis = 0;
        }
    }
Native消息机制
其实在Android 消息处理机制中,不仅包括了Java层的消息机制处理,还包括了Native消息处理机制(与我们知道的Handler机制一样,也拥有Handler、Looper、MessageQueue)。这里我们不讲Native消息机制的具体代码细节,如果有兴趣的小伙伴,请查看----->深入理解Java Binder和MessageQueue
这里我们用一张图来表示Native消息与Jave层消息的关系(这里为大家提供了Android源码,大家可以按需下载),具体细节如下图所示:

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);
}
在nativePollOnce()方法中调用nativeMessageQueue的pollOnce()方法,我们接着走。
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;
    }
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        // 先处理没有Callback方法的 Response事件
        while (mResponseIndex < mResponses.size()) {
            const Response& response = mResponses.itemAt(mResponseIndex++);
            int ident = response.request.ident;
            if (ident >= 0) { //ident大于0,则表示没有callback, 因为POLL_CALLBACK = -2,
                int fd = response.request.fd;
                int events = response.events;
                void* data = response.request.data;
                if (outFd != NULL) *outFd = fd;
                if (outEvents != NULL) *outEvents = events;
                if (outData != NULL) *outData = data;
                return ident;
            }
        }
        if (result != 0) {
            if (outFd != NULL) *outFd = 0;
            if (outEvents != NULL) *outEvents = 0;
            if (outData != NULL) *outData = NULL;
            return result;
        }
        // 再处理内部轮询
        result = pollInner(timeoutMillis);
    }
}
nativePollOnce()一直循环为毛不造成主线程的卡死?
Android 消息传递机制的更多相关文章
- Handler Looper源码解析(Android消息传递机制)
		Android的Handler类应该是常用到的,多用于线程间的通信,以及子线程发送消息通知UI线程刷新View等等.这里我主要总结下我对整个消息传递机制,包括Handler,Looper,Messag ... 
- Android消息传递之Handler消息机制
		前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ... 
- Android学习之Handler消息传递机制
		Android只允许UI线程修改Activity里的UI组件.当Android程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户 ... 
- Android异步消息传递机制源码分析
		1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ... 
- (Android数据传递)Intent消息传递机制 “Intent”“数据传递”
		Intent类的继承关系: 需要注意的是,该类实现了Parcelable(用于数据传递)和Cloneable接口. Intent是一种(系统级别的)消息传递机制,可以在应用程序内使用,也可以在应用 ... 
- Android学习笔记-事件处理之Handler消息传递机制
		内容摘要:Android Handler消息传递机制的学习总结.问题记录 Handler消息传递机制的目的: 1.实现线程间通信(如:Android平台只允许主线程(UI线程)修改Activity里的 ... 
- Android消息传递之基于RxJava实现一个EventBus - RxBus
		前言: 上篇文章学习了Android事件总线管理开源框架EventBus,EventBus的出现大大降低了开发成本以及开发难度,今天我们就利用目前大红大紫的RxJava来实现一下类似EventBus事 ... 
- Android消息传递之EventBus 3.0使用详解
		前言: 前面两篇不仅学习了子线程与UI主线程之间的通信方式,也学习了如何实现组件之间通信,基于前面的知识我们今天来分析一下EventBus是如何管理事件总线的,EventBus到底是不是最佳方案?学习 ... 
- Android消息传递之组件间传递消息
		前言: 上篇学习总结了Android通过Handler消息机制实现了工作线程与UI线程之间的通信,今天来学习一下如何实现组件之间的通信.本文依然是为学习EventBus做铺垫,有对比才能进步,今天主要 ... 
随机推荐
- 【CF 482E】ELCA
			题意 题解 50pts 由于这题 \(2s\),所以可以信仰一波,暴力修改.查询. 暴力修改的复杂度是 \(O(n)\),暴力查询的复杂度是 \(O(n^2)\). 但不难发现可以通过记录子树大小来优 ... 
- xss过滤方法
			用的白名单过滤,是我们的论坛自用的方法,也许考虑不周,欢迎来黑我们的论坛!https://www.ebcms.com/forum.html // 安全过滤 function safe_html($ht ... 
- 欧拉函数 || [SDOI2008]仪仗队 || BZOJ 2190 || Luogu P2158
			题面:P2158 [SDOI2008]仪仗队 题解: 显然除了(1,1),(0,1),(1,0)三个点外,对于其他点(x,y)只要满足gcd(x,y)==1就可以被看到 然后这些点是关于y=x对称的, ... 
- java调用js函数
			问题:js函数可能有多个,之间有相互调用关系,有jquery jar包 org.mozilla.javascript-1.7.2 js envjs-1.2.js java代码 import jav ... 
- HDU - 6087 Rikka with Sequence (可持久化treap+倍增+重构)
			题目链接 感谢Dream_Lolita的题解,经过无数次失败的尝试之后终于AC了... 线段树是维护区间信息的强大工具,但它的形态是固定的,只支持修改和删除操作,不支持插入.反转.复制.分裂合并等操作 ... 
- Linux设备驱动学习笔记
			之前研究Linux设备驱动时做的零零散散的笔记,整理出来,方便以后复习. 1.1驱动程序的的角色 提供机制 例如:unix图形界面分为X服务器和窗口会话管理器 X服务器理解硬件及提供统一的接口给用户程 ... 
- PLC与PC通讯
			using System; using System.Windows.Forms; using Microsoft.Win32; // for the registry table using Sys ... 
- react -搭建服务
			import 'whatwg-fetch'; import 'es6-promise'; require('es6-promise').polyfill(); import * as common f ... 
- redis过期策略、内存淘汰策略、持久化方式、主从复制
			原文链接:https://blog.csdn.net/a745233700/article/details/85413179 一.Redis的过期策略以及内存淘汰策略:1.过期策略:定期删除+惰性删除 ... 
- BZOJ 3173: [Tjoi2013]最长上升子序列 Splay
			一眼切~ 重点是按照 $1$~$n$ 的顺序插入每一个数,这样的话就简单了. #include <cstdio> #include <algorithm> #define N ... 
