消息处理机制(AOSP4.4.2)
-
Android 应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队列,应用程序的主线程,不断地从这个消息队列中获取消息(Looper),然后对消息进行处理(Handler)。
-
消息存放在一个消息队列中(MessageQueue),应用程序的主线程围绕消息队列进入无限循环,直到应用程序退出。
-
如果队列中有消息,应用程序的主线程会把它取出,并分发给相应的 Handler 进行处理;如果队列中没有消息,应用程序的主线程会进入空闲等待状态,等待消息的到来。
-
Android 应用程序的消息处理机制是由消息循环、发送和处理这三个部分组成。
-
网络文章
- HandlerThread详解 - 简书 (jianshu.com)
- HandlerThread详解 - 简书 (jianshu.com)
一、创建主线程消息队列
-
Java 层的 Looper 类和 MessageQueue 类是通过 C++ 层实现的。C++ 层有一个相应的 Looper 类和 NativeMessageQueue 类。
-
Java 层 Looper 对象内部的成员变量 mQueue ,它指向了一个 MessageQueue 对象;而在 C++ 层中,每一个 NativeMessageQueue 对象内部都有一个成员变量 mLooper,它指向了一个 C++ 层的 Looper 对象。
1——3 Java层
1. ActivityThread.main
//frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread {
......
public static void main(String[] args) {
......
/* 1.为当前线程初始化Looper */
Looper.prepareMainLooper();
......
/* 2.使主线程进入消息循环中 */
Looper.loop();
......
}
}
2. Looper.prepareMainLooper
- 只能在主线程调用,即 UI 线程,其他线程通过 Looper.getMainLooper() 方法访问主线程的 Looper。
- 成员变量 sThreadLocal 的类型为 ThreadLocal ,这是一个线程局部变量,保证每一个调用 prepare 函数的线程,都有一个独立的 Looper 对象。Java中的ThreadLocal详解 - 夏末秋涼 - 博客园 (cnblogs.com)
- 因为 Looper 类的构造方法是 private 的,假若通过类似单例模式的写法,多个线程修改同一个 Looper 实例显然不符合设计需求。
//frameworks/base/core/java/android/os/Looper.java
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
private Looper(boolean quitAllowed) {
/* 3.创建Looper对象时,会同时创建一个消息队列MessageQueue,
保存在Looper的成员变量mQueue中,消息就存放在这个队列。*/
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
......
}
/* 2.函数prepare在线程中创建一个Looper对象,
这个对象存放在sThreadLocal成员变量里。*/
sThreadLocal.set(new Looper(quitAllowed));
}
// 获得与当前线程相关联的一个Looper对象
public static Looper myLooper() {
return sThreadLocal.get();
}
public static void prepareMainLooper() {
/* 1.调用prepare */
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(
"The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
......
}
3.MessageQueue.MessageQueue
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
......
// NativeMessageQueue的地址
private int mPtr; // used by native code
......
private native static int nativeInit();
......
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
/* 1.初始化工作交给Native方法实现 */
mPtr = nativeInit();
}
......
}
4——6 C++层
4. android_os_MessageQueue.cpp : android_os_MessageQueue_nativeInit
- 智能指针:(150条消息) Android 智能指针详解_android 智能指针使用方法_私房菜的博客-CSDN博客
- 指针强制类型转换:C++强制类型转换运算符(static_cast、reinterpret_cast、const_cast和dynamic_cast) (biancheng.net)
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
/* 1.先创建一个NativeMessageQueue对象 */
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
......
/* 2.增加nativeMessageQueue的强指针引用计数 */
nativeMessageQueue->incStrong(env);
/* 3.将nativeMessageQueue地址强制转换,返回 */
return reinterpret_cast<jint>(nativeMessageQueue);
}
......
5. android_os_MessageQueue.cpp :: NativeMessageQueue
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
/* 1.调用静态成员函数getForThread来检查是否已经为
当前线程创建过一个C++层的Looper对象。 */
mLooper = Looper::getForThread();
if (mLooper == NULL) {
/* 2.没有即创建,保存在成员变量mLooper */
mLooper = new Looper(false);
/* 3.与当前线程关联 */
Looper::setForThread(mLooper);
}
}
......
6. Looper.cpp :: Looper
- 管道在一个线程的消息循环过程中的作用:
- 当一个线程没有新消息要处理时,它会在这个管道的读端文件描述符上睡眠,直到有新的消息要处理;
- 当其他线程向本线程发送了一个消息后,其他线程会通过管道的写端文件描述符,往管道写入数据,从而将这个线程唤醒。
- fcntl:file control ,提供了对文件描述符的各种操作。(150条消息) Unix/Linux编程:fcntl函数总结_fcntl全称_OceanStar的学习笔记的博客-CSDN博客
- Linux 系统的 epoll :为了同时监听多个文件描述符的 IO 读写事件而设计,它是一个多路复用的 IO 接口。Linux epoll模型 - venow - 博客园 (cnblogs.com)
- 多路复用:单线程或单进程同时监测若干个文件描述符,可以执行IO操作。
- 文件描述符:Linux 的所有对设备和文件的操作都是使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值, 并指向在内核中每个进程打开文件的记录表。
//system/core/libutils/Looper.cpp
......
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
int wakeFds[2];
/* 1.创建一个管道 */
int result = pipe(wakeFds);
......
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
/* 2.O_NONBLOCK使I/O变成非阻塞模式(non-blocking),在读取
不到数据或是写入缓冲区已满会马上return,而不会阻塞等待。*/
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
......
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
......
/* 3.创建一个epoll实例,并将它的文件描述符保存在成员变量
mEpollFd */
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
......
/* 4.将创建的管道读端文件描述符添加到epoll实例中,
以便epoll对管道的写操作进行监听 */
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event));
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeReadPipeFd;
// EPOLL_CTL_ADD宏:注册新的fd到epoll
result = epoll_ctl(
mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
......
}
......
二、消息循环
1——2 Java层
1. Looper.loop
//frameworks/base/core/java/android/os/Looper.java
public final class Looper {
......
public static void loop() {
final Looper me = myLooper();
......
/* 1.获得当前线程的消息队列 */
final MessageQueue queue = me.mQueue;
......
/* 2.不断检查这个消息队列中是否有消息要处理 */
for (;;) {
// 如果没有消息要处理,会进入睡眠等待状态
Message msg = queue.next(); // might block
// 如果消息为null,退出消息循环
if (msg == null) {
return;
}
......
// 否则的话就调用target对象的dispatchMessage
// 来处理这个消息,这个target对象的类型为Handler
msg.target.dispatchMessage(msg);
......
msg.recycle();
}
}
......
}
2. MessageQueue.next
- 调用这个函数的时候,有可能会让线程进入等待状态。
- 线程会进入等待状态的情况:
- 消息队列中没有消息时,它会使线程进入等待状态;
- 消息队列中有消息,但是消息指定了执行的时间,而现在还没有到这个时间,线程也会进入等待状态(消息队列中的消息是按时间先后来排序的)。
- 同步拦截器:它可以拦截它之后的所有同步消息,直到该拦截器被移除。消息拦截器和普通消息的差异在于,拦截器的 target 是空的,正常通过 enqueueMessage 方法入队的消息限制 target 不能为空。
- Android 学习笔记之Handler 总结 - 掘金 (juejin.cn)
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
......
// 用来描述当前线程需要处理的消息
Message mMessages;
......
private native static void nativePollOnce(
int ptr, int timeoutMillis);
......
Message next() {
// 用来保存:注册到消息队列中,IdleHandler的个数
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// 当消息队列中没有新消息要处理时,当前线程需要进入睡眠
// 等待状态的时间:-1一直阻塞;0不阻塞;>0阻塞指定时间
/* 1.初始设置为0 */
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
// 为阻塞做准备,把正在等待处理的
// Binder IPC请求都释放了,避免
// 长时间得不到处理
Binder.flushPendingCommands();
}
/* 2.检查当前线程的消息队列中是否有新消息要处理。
参数mPtr是在C++层创建的NativeMessageQueue */
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
/* 3.判断队头是不是同步拦截器 */
if (msg != null && msg.target == null) {
// 同步屏障,寻找第一条异步消息。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
/* 4.处理消息 */
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;
......
// 设置为in-use状态
msg.markInUse();
// 返回取出的消息
return msg;
}
} else {
// 如果消息队列中没有可以取出的消息,nextPollTimeoutMillis
// 等于-1,让线程一直阻塞。
nextPollTimeoutMillis = -1;
}
// 如果队列已经退出了直接注销、结束方法
if (mQuitting) {
dispose();
return null;
}
// --- IdleHandler的处理 --------------------------------
......
}
......
/* 5.执行了IdleHandler代码块,有可能有新的消息入队了,
不阻塞线程,去查看有没有新消息 */
nextPollTimeoutMillis = 0;
}
}
......
}
3——7 C++层
3. android_os_MessageQueue.cpp : android_os_MessageQueue_nativePollOnce
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
// 参数ptr是Java层MessageQueue对象的成员变量mPtr,它保存的是一个C++层
// 创建的NativeMessageQueue对象的地址值
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env,
jclass clazz,
jint ptr,
jint timeoutMillis) {
/* 1.将ptr转化为NativeMessageQueue对象 */
NativeMessageQueue* nativeMessageQueue =
reinterpret_cast<NativeMessageQueue*>(ptr);
/* 2.调用函数来检查是否有新消息进行处理 */
nativeMessageQueue->pollOnce(env, timeoutMillis);
}
......
4. android_os_MessageQueue.cpp :: pollOnce
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {
......
/* 1.调用Looper类的pollOnce */
mLooper->pollOnce(timeoutMillis);
......
}
......
5. Looper.cpp :: pollOnce
//system/core/libutils/Looper.cpp
......
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
......
if (result != 0) {
......
return result;
}
/* 1.循环不断地调用,检查当前线程是否有新的消息要处理,
如果有,那么返回值不为0,就会跳出循环,以便当前线程对
新的信息进行处理 */
result = pollInner(timeoutMillis);
}
}
......
6. Looper.cpp :: pollInner
//system/core/libutils/Looper.cpp
......
int Looper::pollInner(int timeoutMillis) {
......
// Poll.
int result = ALOOPER_POLL_WAKE;
......
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
/* 1.监听注册在epoll实例中的文件描述符的IO读写事件。
如果没有发生IO读写事件,那么当前线程就会在这里进入睡
眠等待状态,timeoutMillis指定其时间。 */
int eventCount = epoll_wait(
mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
......
/* 2.检查哪一个文件描述符发生了IO读写事件 */
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
// 检查IO读写事件的文件描述符是不是与当前线程
// 关联的一个管道的读端文件描述符
if (fd == mWakeReadPipeFd) {
// 检查IO读写事件的类型是否为EPOLLIN
if (epollEvents & EPOLLIN) {
// 这时说明其他线程向与当前线程相
// 关联的管道,写入了新的数据
awoken();
} ......
} ......
}
......
return result;
}
......
7. Looper.cpp :: awoken
//system/core/libutils/Looper.cpp
......
void Looper::awoken() {
......
char buffer[16];
ssize_t nRead;
do {
/* 1.调用函数将与当前线程所关联的管道的数据读出。
就是简单读出数据,不关心数据本身 */
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
......
三、消息发送
- 应用程序的主线程准备好消息队列,并且进入到消息循环后,其它地方就可以往这个消息队列中发送消息了。
- 以应用程序的启动为例,说明应用程序是如何把消息加入到应用程序的消息队列中去的。
1——4 Java层
1. ActivityThread.ApplicationThread.scheduleLaunchActivity
//frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread {
......
private class ApplicationThread extends ApplicationThreadNative {
......
public final void scheduleLaunchActivity(Intent intent,
IBinder token,int ident,
ActivityInfo info,
Configuration curConfig,
CompatibilityInfo compatInfo,
int procState, Bundle state,
List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents,
boolean notResumed,
boolean isForward,
String profileName,
ParcelFileDescriptor profileFd,
boolean autoStopProfiler) {
updateProcessState(procState, false);
/* 1.把相关的参数封装成一个ActivityClientRecord对象 */
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profileFile = profileName;
r.profileFd = profileFd;
r.autoStopProfiler = autoStopProfiler;
updatePendingConfiguration(curConfig);
/* 2.调用sendMessage函数发送消息H.LAUNCH_ACTIVITY */
sendMessage(H.LAUNCH_ACTIVITY, r);
}
......
}
......
}
2. ActivityThread.ApplicationThread.sendMessage
- 这里的 mH 是 ActivityThread 类的成员变量,它的类型为 H ,继承于 Handler 类。
//frameworks/base/core/java/android/app/ActivityThread.java
private class ApplicationThread extends ApplicationThreadNative {
......
private void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0, 0, false);
}
......
private void sendMessage(int what, Object obj, int arg1, int arg2,
boolean async) {
......
/* 1.把传进来的参数封装成一个Message对象msg */
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
/* 2.通过mH.sendMessage函数把消息对象msg
加入消息队列中 */
mH.sendMessage(msg);
}
......
}
3. Handler.sendMessage
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
......
public final boolean sendMessage(Message msg){
/* 1.在发送消息时,可以指定消息的处理时间,sendMessage
发送消息的默认处理时间为当前时间,表示马上处理。因此,
这里传入的时间参数为0 */
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(
Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
/* 2.先获得当前时间,然后加上消息要延时处理的时间,得到处理
这个消息的绝对时间。然后把消息加入到消息队列中 */
return sendMessageAtTime(msg, SystemClock.uptimeMillis() +
delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
/* 3.首先得到应用程序的消息队列mQueue,它是在Handler对象
构造时初始化的 */
MessageQueue queue = mQueue;
......
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg,
long uptimeMillis) {
/* 4.设置这个消息的目标对象target,即这个消息最终是由谁
来处理 */
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
......
}
4. MessageQueue.enqueueMessage
- 把消息加入到消息队列时,分两种情况:
- 第一种,当前消息队列为空:这时应用程序的主线程一般处于空闲等待状态,这时要唤醒它。
- 第二种,消息队列不为空:这时不需要唤醒应用程序的主线程,因为这时它一定是在处理消息队列中的消息,因此不会处于空闲等待状态。
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
......
private native static void nativeWake(int ptr);
......
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
msg.when = when;
Message p = mMessages;
boolean needWake;
/* 1.根据实际情况将一个消息插入到一个消息队列中:
(1)消息队列为空;
(2)插入的消息处理时间为0;
(3)插入的消息的处理时间,小于保存在消息队列头
的消息的处理时间;
(4)插入的消息的处理时间,大于等于保存在消息队
列头的消息的处理时间;*/
if (p == null || when == 0 || when < p.when) {
// (1)~(3):插入的消息保存在消息队列
// 的头部
msg.next = p;
mMessages = msg;
// mBlocked记录了目标线程是否处于睡眠等待
needWake = mBlocked;
} else {
// (4):插入的消息保存在消息队列的某一
// 个位置上。通过循环找出。
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;
}
/* 2.把消息加入到消息队列后,可能需要唤醒
目标线程,分两种情况:
(1)插入的消息在消息队列中间;
(2)插入的消息在消息队列头部。
对于(1):由于消息队列头部消息没有变化,
所以无论如何都不需要唤醒目标线程。
对于(2):头部消息发生了变化,需要唤醒目
标线程,以便对于头部新消息进行处理,如果
已唤醒就不用了 */
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
......
}
5——7 C++层
5. android_os_MessageQueue.cpp : android_os_MessageQueue_nativeWake
//home/yy/aosp/frameworks/base/core/jni/android_os_MessageQueue.cpp
......
static void android_os_MessageQueue_nativeWake(JNIEnv* env,
jclass clazz,
jint ptr) {
/* 1.转型 */
NativeMessageQueue* nativeMessageQueue =
reinterpret_cast<NativeMessageQueue*>(ptr);
/* 2.唤醒 */
return nativeMessageQueue->wake();
}
......
6. android_os_MessageQueue.cpp : wake
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
void NativeMessageQueue::wake() {
mLooper->wake();
}
......
7. Looper.cpp : wake
//system/core/libutils/Looper.cpp
......
void Looper::wake() {
......
ssize_t nWrite;
do {
/* 1.向管道的写端文件描述符写入一个新的数据,
这时目标线程就会因为管道发生了一个IO写事件而
被唤醒 */
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
......
}
......
四、消息处理
- 在消息循环中,当一个线程没有新的消息需要处理时,就会在 C++ 层的 pollInner 函数中进入睡眠等待状态;
- 当该线程有新的消息要处理时,它首先会在 C++ 层的 pollInner 函数中唤醒,然后沿着调用链返回到 Java 层的 loop 中,对新消息进行处理。
1——2
1. Looper.loop
//frameworks/base/core/java/android/os/Looper.java
public class Looper {
......
public static void loop() {
/* 1.得到当前线程下的Looper */
final Looper me = myLooper();
......
/* 2.得到与Looper对应的MessageQueue */
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
/* 3.得到当前线程的唯一标识 */
final long ident = Binder.clearCallingIdentity();
/* 4.不断取出消息 */
for (;;) {
// 从队列中取出一个消息
Message msg = queue.next();
// 消息为空,表示退出消息循环
if (msg == null) {
return;
}
......
// 从消息队列中获得消息后,就会调用它的
// target成员变量
msg.target.dispatchMessage(msg);
......
// 处理完毕,回收消息
msg.recycle();
}
}
......
}
2. Handler.dispatchMessage
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
......
public void dispatchMessage(Message msg) {
// 如果msg的回调接口不空
if (msg.callback != null) {
// 运行这个callback
handleCallback(msg);
} else {
// 如果mCallback不空,说明Handler设置了
// callback接口,先执行接口方法
if (mCallback != null) {
// 如果接口处理完返回true说明它把消息拦截
// 不再执行Handler自身的处理消息方法
if (mCallback.handleMessage(msg)) {
return;
}
}
// 调用Handler自身处理消息的方法
handleMessage(msg);
}
}
......
}
2.1 msg.callback不空
- Message 类的成员变量 callback 指向的是一个 Runnable 对象,所以这种情况是将消息交给了 Runnable 类的 run 方法执行。
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
......
/* 1.调用post发送一个消息时,Handler会指定一个
回调接口*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
......
/* 2.将post传来的Runnable对象封装成一个Message */
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
......
/* 3.Message的callback执行run方法 */
private static void handleCallback(Message message) {
message.callback.run();
}
......
}
2.2 mCallback不空
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
......
/* 1.定义了一个Callback接口 */
public interface Callback {
public boolean handleMessage(Message msg);
}
......
/* 2.可以调用构造方法,并且指定Callback */
public Handler(Callback callback) {
this(callback, false);
}
......
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
/* 3.保存在成员变量mCallback中 */
mCallback = callback;
mAsynchronous = async;
}
......
}
2.3 handleMessage
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
......
/* 1.是一个空实现。一般用Handler的子类发送消息,
子类重写该方法处理消息。*/
public void handleMessage(Message msg) {
}
......
}
五、空闲消息(IdleHandler)
- 特殊的消息,不用事先发送到一个消息队列,而是由一个线程在空闲时发送出来。
- 由空闲消息处理器对象来处理,必须实现 IdleHandler 接口。
- 使用时需要注册到线程的消息队列中,即注册到与这个线程所关联的一个 MessageQueue 对象中。
- 将不重要、不紧急的事情放在线程空闲时来执行,可以充分地利用线程的空闲时间。
定义与使用
- MessageQueue 提供了 addIdleHandler 和 removeIdleHandler 来注册和删除 IdleHandler。
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
......
// 保存线程的空闲消息处理器
private final ArrayList<IdleHandler> mIdleHandlers =
new ArrayList<IdleHandler>();
......
public static interface IdleHandler {
......
// 接收空闲消息。
// 执行这个函数时,如果返回值为false,那么就会将其从mIdleHandlers移除
// 否则,会在应用程序中继续维护这个IdleHandler,下次空闲时再次执行
boolean queueIdle();
}
// 注册一个空闲消息处理器
public void addIdleHandler(IdleHandler handler) {
if (handler == null) {
throw new NullPointerException(
"Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
// 注销一个空闲消息处理器
public void removeIdleHandler(IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
......
}
消息循环的处理
- pendingIdleHandlerCount 为 0 的两种情况:
- 消息队列中没有注册的空闲消息处理器;
- 已经发送过一个线程空闲消息。
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
......
Message next() {
// 用来保存:注册到消息队列中,IdleHandler的个数
int pendingIdleHandlerCount = -1; // -1 only during first iteration
......
for (;;) {
......
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
......
// --- IdleHandler的处理 --------------------------------
/* 1.进入阻塞状态前,检查有没有IdleHandler需要执行 */
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
// 得到IdleHandler的数量
pendingIdleHandlerCount = mIdleHandlers.size();
}
/* 2.pendingIdleHandlerCount<=0说明没有
IdleHandler需要执行的 */
if (pendingIdleHandlerCount <= 0) {
// 直接进入下次循环阻塞线程
mBlocked = true;
continue;
}
/* 3.执行到此处说明有待处理的IdleHandler,那就从
IdleHandler集合列表中取出 */
if (mPendingIdleHandlers == null) {
// 初始化
mPendingIdleHandlers = new
IdleHandler[Math.max(
pendingIdleHandlerCount, 4)];
}
/* 4.从IdleHandler集合中获取待处理的IdleHandler */
mPendingIdleHandlers =
mIdleHandlers.toArray(mPendingIdleHandlers);
}
/* 5.执行IdleHanlder */
for (int i = 0; i < pendingIdleHandlerCount; i++) {
// 取出一个IdleHandler
final IdleHandler idler = mPendingIdleHandlers[i];
// 释放掉引用
mPendingIdleHandlers[i] = null; // 释放引用handler
// IdleHandler的执行模式,true执行一次,false总是执行
boolean keep = false;
try {
// 执行,并得到执行模式
keep = idler.queueIdle();
} ......
// 通过执行模式判断是否需要移除
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
/* 6.处理完了所有IdleHandler把数量清零 */
pendingIdleHandlerCount = 0;
......
}
}
......
}
六、总结
- Android 应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。
- Android 应用程序的主线程在进入消息循环过程前,会在内部创建一个 Linux 管道(Pipe),这个管道的作用是,使得 Android 应用程序的主线程,在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列,在有消息需要处理时唤醒应用程序的主线程。
- Android 应用程序的主线程进入空闲等待状态的方式,实际上就是在管道的读端等待管道中有新的内容可读,具体来说就是是通过 Linux 系统的 Epoll 机制中的 epoll_wait 函数进行的。
- 当往 Android 应用程序的消息队列中加入新的消息时,会同时往管道中的写端写入内容,通过这种方式就可以唤醒正在等待消息到来的应用程序主线程。
- 当应用程序主线程在进入空闲等待前,会认为当前线程处理空闲状态,于是就会调用那些已经注册了的 IdleHandler 接口,使得应用程序有机会在空闲的时候处理一些事情。