一、Looper
1、sThreadLocal
//【分析点1】
//为了实现线程隔离而使用ThreadLocal
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
2、Looper
私有构造函数,外部初始化
Looper
只能通过prepare
方法
//【分析点2】
//构造函数是内部私有的,也就是说外部是不可调用构造函数的
//这就决定了每个Looper只会对应一个MessageQueue
private Looper(boolean quitAllowed) {
//创建MessageQueue
mQueue = new MessageQueue(quitAllowed);
//当前线程
mThread = Thread.currentThread();
}
3、prepare
普通线程构造
Looper
的方式
//这个才是真正的初始化方法
private static void prepare(boolean quitAllowed) {
//【分析点3】
//ThreadLocal的get方法获取线程的方式:
// Thread t = Thread.currentThread();
//由此可见取的是当前线程内的ThreadLocalMap
//ThreadLocal就是通过这种方式来实现线程隔离
if (sThreadLocal.get() != null) {
//所以这里如果不为空,就说明这个Looper被绑定了
//抛出错误
throw new RuntimeException("Only one Looper may be created per thread");
}
//将Looper存进ThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
4、prepareMainLooper
主线程构造
Looper
的方式,区别在于quitAllowed
参数为false
//【分析点4】
//这是给主线程调用的初始化方法
//主线程和普通线程Looper的最大差别就是
//主线程的Looper不允许退出
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
5、loop(重要)
Looper
关键方法
public static void loop() {
final Looper me = myLooper();
...
//【分析点5】
//通过for循环从MessageQueue的next方法里面取消息
//可以看到这是一个死循环,也就是Android程序为什么不像普通Java程序那样运行完就退出的原因
for (;;) {
//【分析点6】
//调用MessageQueue的next方法
//这里要留意的是,我们平常说的阻塞并不是在
//Looper的loop方法里面,而是在queue的next方法里面,
//这个后续会讲到
Message msg = queue.next();
if (msg == null) {
return;
}
//【分析点7】
//这是给BlockCanary之类性能检查工具用来检测卡顿的
//原理就是通过在dispatch前后做打印Log,从而得到
//处理一个消息的时间
//与此类似的性能检测方案还有监听Choreographer的帧率
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
try {
//【分析点8】
//分发方法
//这个target其实就是在Handler的enqueueMessage里面
//Handler将自己传给Message
//这里也是Handler会内存泄漏的原因
//通过Handler.dispatch方法
//从而调用到Runnable.run / Callback.handleMessage / handleMessage
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
...
} finally {
...
}
//对应【分析点7】
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//【分析点8】
//我们用Message通常都是通过obtain方法
//就是因为Meesage内部持有一个长度为50的消息缓存池(其实是单链表)
//这里调用回收方法
msg.recycleUnchecked();
}
}
二、MessageQueue
1、native method
C++
层面的方法
//【分析点9】
//这些都是c++层面的方法,我通过AndroidXRef找到了源码
private native static long nativeInit();
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static void nativeDestroy(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
1.1、nativeInit
创建
mWakeEventFd
文件描述符,并通过epoll_ctl
方法监听文件描述符
//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
//在这里我们可以看到,NativeMessageQueue是Message在cpp层的具体实现
//接着我们看看NativeMessageQueue的构造函数
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
...
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
//可以看到,不止是MessageQueue,Looper也是有对应实现的
//继续看Looper的实现
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
//源码位置:/system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
//【分析点10】
//调用eventfd返回一个文件描述符,专门用于事件通知。
...
//进入rebuildEpollLocked
rebuildEpollLocked();
}
//源码位置:/system/core/libutils/Looper.cpp
void Looper::rebuildEpollLocked() {
//【分析点11】
//终于看到epoll了
//这里做了:
//1、通过epoll_create创建 epoll 对象
//2、调用epoll_ctl将mWakeEventFd设置为要监听的文件描述符
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
...
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
...
}
1.2、nativePollOnce
通过
epoll_wait
进入休眠状态,同时释放CPU
//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
//主要还是通过调用nativeMessageQueue的方法
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
//最终还是调用Looper的pollOnce
mLooper->pollOnce(timeoutMillis);
...
}
//源码位置:/system/core/libutils/Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
for (;;) {
...
//调用pollInner
result = pollInner(timeoutMillis);
}
//源码位置:/system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {
...
//【分析点12】
//终于调用epoll的方法epoll_wait
//让出CPU,并让线程进入休眠
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
...
}
}
1.3、nativeWake
通过对
mWakeEventFd
文件描述符发起写操作,从而唤醒线程
//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
//一样的套路,最终都是调用到Looper的方法
nativeMessageQueue->wake();
}
//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {
mLooper->wake();
}
//源码位置:/system/core/libutils/Looper.cpp
void Looper::wake() {
uint64_t inc = 1;
//【分析点13】
//通过对nativeInit创建的mWakeEventFd发起一个写操作
//从而唤醒nativePollOnce中调用epoll_wait方法的线程
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
...
}
2、MessageQueue
MessageQueue
构造函数
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
3、next(重要)
真正阻塞的方法,主要做了几件事情:
- 判断是否同步屏障消息,如果是,则找出最近的一条异步消息
- 如果消息还没到时间执行,则设置nextPollTimeoutMillis,让下次进入next方法时阻塞
- 如果已经可以执行了,就直接return这条消息
- 如果没有消息,进入永久阻塞,直到被唤醒
- 处理闲时任务
Message next() {
//【分析点14】
//next方法主要有几大逻辑:
//1、判断是否同步屏障消息,如果是,则找出最近的一条异步消息
//2、如果消息还没到时间执行,则设置nextPollTimeoutMillis,让下次进入next方法时阻塞
//如果已经可以执行了,就直接return这条消息
//3、如果没有消息,进入永久阻塞,直到被唤醒
//4、处理闲时任务
...
int nextPollTimeoutMillis = 0;
//【分析点15】
//跟Looper的loop一样,这里也是一个无限循环
for (;;) {
//【分析点16】
//1、这里表明所有的消息都已经处理完了,在等待新的消息进入,如果
//nextPollTimeoutMillis参数为-1,则代表无限期的阻塞,直到被唤醒
//2、这里也就是真正阻塞消息的地方
//nativePollOnce是一个IO多路复用机制
//IO多路复用可以简单地理解为用一个线程去监视多个文件句柄(Linux下一切皆文件)
//只有这个文件句柄就绪,才会被唤醒,否则这个文件句柄会交出CPU并阻塞程序
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//【分析点17】
//这里判断通过target是否为空,判断是否同步屏障消息,
//如果是,通过while循环找到下一条 异步 消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//【分析点18】
//如果还没到时间执行这条消息,则进入有时间
//的阻塞(也就是等待)
if (now < msg.when) {
nextPollTimeoutMillis =
(int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//如果没有设置延时,那就将msg return出去
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
//【分析点19】
//msg == null,也就是说已经没有消息了
//进入永久阻塞,直到被nativeWake唤醒
nextPollTimeoutMillis = -1;
}
//【分析点20】
//调用quit的时候,mQuitting才会为true
//这里如果返回null,也就是next返回null,会导致
//Looper结束loop
//所以Looper的关闭流程分两个:
//1、调用Looper.quit / Looper.quitSafely,导致next的时候MessageQueue返回一个
//null消息
//2、Looper.loop发现msg==null,就会return,退出loop方法
if (mQuitting) {
dispose();
return null;
}
//【分析点21】
//处理闲时任务
//如果等待执行中的闲时任务没有了,那就取出闲时
//任务handler中的任务
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
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;
boolean keep = false;
try {
//【分析点22】
//这里要注意,执行闲时任务的时候,因为这是在主线程,所以不能太
//耗时,避免影响到下一条消息
keep = idler.queueIdle();
}
if (!keep) {
synchronized (this) {
//每执行完一条,就删除一条
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
//阻塞时间改为0,即不阻塞
//但是事实上阻不阻塞,重新进入方法后会再决定
nextPollTimeoutMillis = 0;
}
}
4、quit
退出方法,主要分
安全退出
和非安全退出
退出流程:将mQuitting
设置为true
,等到Looper
下次调用next
的时候,next
方法发现mQuitting
为true
,就会给Looper
返回一个null
的信息,Looper
接收到null
消息就会调用return
退出for
循环
void quit(boolean safe) {
//【分析点18】
//主线程是不允许退出的
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//【分析点19】
//安全与非安全的区别就在于
//安全:先清除还没到执行时间的消息,会等当前正在要取出
//的消息执行完
//非安全:一律清除
if (safe) {
//安全退出
removeAllFutureMessagesLocked();
} else {
//非安全退出
removeAllMessagesLocked();
}
nativeWake(mPtr);
}
}
5、removeAllFutureMessagesLocked
安全退出,只会清除那些还没到执行时间的消息
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
//【分析点20】
//1、因为在enqueueMessage的时候已经将所有Message按照when字段进行排序了
//所以如果头节点也还没到执行时间,那说明后续的消息也未到执行时间
//所以直接清除所有消息
//2、如果第一个本该执行了,那就从第二个开始,依旧是将尚未可执行的消息置空
//然后回收Message
Message p = mMessages;
if (p != null) {
//如果消息的执行时间还没到
//清空所有消息
if (p.when > now) {
removeAllMessagesLocked();
} else {
//否则遍历第二个消息开始的所有消息
//如果执行时间还没到,就置空并回收
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
6、removeAllMessagesLocked
非安全退出,不管消息是否已经到可以执行的时间
private void removeAllMessagesLocked() {
Message p = mMessages;
//【分析点21】
//遍历整个链表,将消息置空并回收
//不论这个消息是否可立刻执行
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
7、enqueueMessage(重要)
消息入队方法,在这里根据
msg.when
对消息进行排序
注:同步屏障并不通过这个方法入队
boolean enqueueMessage(Message msg, long when) {
//【分析点22】
//这个方法将同步和异步消息按照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) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//【分析点23】
//(1)消息头为空,说明消息队列已经没消息了,正处于阻塞状态
//(2)不是延时消息
//(3)是延时,但是比队列头的消息还早执行
//则将这条消息放在消息队列的第一个
//如果阻塞状态,则唤醒MessageQueue
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//【分析点24】
//进入这里即代表这是一条普通的延时任务
//则遍历链表,根据时间顺序来插入
//唤醒条件:已经阻塞 & 消息链表头部节点是一个同步屏障节点 & 新入队的消息为异步消息
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
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
8、postSyncBarrier
发送同步屏障消息
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
//【分析点25】
//Message主要通过构造函数和obtain方法获取
//系统更推荐我们通过obtain方法获取,因为这是从缓存链表里面取出来的
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
//【分析点26】
//这部分的逻辑相当于普通消息在enqueueMessage里被排序一样
//就算有多个同步屏障消息A、B、C、D,那它们也是根据when字段排序的
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
9、removeMessages
删除指定消息,需同时满足三个条件:
- 通过
指定Handler添加
what
字段相同obj字段要么是null、要么等于我们通过setData设置进来的值
注意:如果what字段传0,有可能会将所有post系列的任务都删了,因为post的消息都是通过getPostMessage得到的,这个方法直接通过obtain获取消息,而obtain获取都是通过recycleUnchecked处理过的、what字段被设置成0的消息,这也算是post和send系列的区别之一吧
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
//链表头
Message p = mMessages;
//【分析点27】
//(1)指定handler内
//(2)what相同
//(3)object为null或者object与消息的obj相等
//遍历所有满足条件的消息,回收掉
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
//上面从链表头开始,找出所有满足条件的消息,然后回收掉
//当某条消息的next不满足条件
//进入这个循环
//遍历这条信息后面所有符合条件的消息,并回收
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
10、removeCallbacksAndMessages
删除同时满足这两个条件的消息:
- 通过
指定Handler添加
obj字段要么是null、要么等于我们通过setData设置进来的值
实现逻辑和
removeMeesge
完全一样,只是这个方法不需要指定what
字段
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
//注意这个&&符号,同时满足条件才会remove
//同时注意||,说明object可以是null也可以是具体值,并不妨碍删除消息
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
while (p != null) {
Message n = p.next;
if (n != null) {
//这里也一样,同时满足条件
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
11、getPostMessage
post系列方法
创建msg
的方式,这里要注意创建出来的msg.what
都会是0
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
三、Handler
1、CallBack
内部类,
Handler
在被Looper
调用dispatchMessage
方法后有三种处理方式:
- 调用
msg.callback.run
,也就是我们通过post
进来的Runnable
- 调用内部类
CallBack.handleMessage
- 调用重写方法
handleMessage
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
2、dispatchMessage
在
Looper.loop
中调用的分发方法
public void dispatchMessage(@NonNull Message msg) {
//不管是post还是send,最终在Looper层面,都是调用
//msg.target.dispatchMessage
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//这是设置了Handler.CallBack的情况
if (mCallback.handleMessage(msg)) {
return;
}
}
//这是重写方法
handleMessage(msg);
}
}
3、createAsync
主要作用是使所有通过这个
Handler
发送的Message
,都会被设置为FLAG_ASYNCHRONOUS
异步消息(默认是同步消息),在搭配消息屏障使用的情况下,会被优先调用。
public static Handler createAsync(@NonNull Looper looper) {
return new Handler(looper, null, true);
}
4、obtainMessage
返回一个从缓存链表里面取出来的
Message
public final Message obtainMessage(){
return Message.obtain(this);
}
5、post
可以看到这一系列方法的
msg
是通过getPostMessage
创建出来的
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
6、postAtTime
post
一个在某个具体时间执行的消息,其中时间是通过SystemClock.uptimeMillis() + uptimeMills
计算出来的
关于
SystemClock.uptimeMillis
和System.currentTimeMillis
:
SystemClock.uptimeMillis
是指从开机到现在的毫秒数(手机睡眠的时间不包括在内);System.currentTimeMillis
是指从1970年1月1日 UTC到现在的毫秒数;
其中
System.currentTimeMillis是可以通过System.setCurrentTimeMillis修改的,所以大部分系统源码都不会用这个方法计算时间
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
7、postDelayed
基本同
postAtTime
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
8、postAtFrontOfQueue(重要)
Handler
实现“插队”主要有以下几种方法:
postAtFrontOfQueue
,实际实现也是通过sendMessageAtFrontOfQueue
sendMessageAtFrontOfQueue
,具体实现就是将when
字段设置成0
,其它消息的when
字段都是当前时间或者当前时间往后的,设置为0
就可以确保在enqueueMessage
里面会被排到链表的头部postSyncBarrier
:发送同步屏障消息,然后再发送异步消息
public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
9、runWithScissors(不常见)
这是一个被添加了
@hide
注解的方法,也就是说这个方法是不允许被应用层调用的
这个方法用于:在当前A线程向B线程发送一个Runnable,等待B线程执行完之后A线程再继续执行(有点像Thread.join)
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
//timeout可以等于0但是不能小于0
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
//【分析点28】
//如果是同一个线程,直接执行就可以了
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
//【分析点29】
//如果不在同一线程
//将Runnable封装成BlockingRunnable,调用postAndWait
//关于BlockingRunnable后续会讲到
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
10、removeCallbacks
调用
MessageQueue
的removeMessage
方法
public final void removeCallbacks(@NonNull Runnable r) {
mQueue.removeMessages(this, r, null);
}
11、removeCallbacksAndMessages
同上
public final void removeCallbacksAndMessages(@Nullable Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
12、sendMessage
send
系列方法,与post
系列不同的点:
参数不同
:send
是自定义msg
的,而post
的msg
是通过getPostMessage
得到的,所以如果removeMessage(0)
的话,会将post
进去的所有消息清空回收掉实现不同
:send
要么在继承handler
类时重写handleMessage
,要么创建Handler
的内部类CallBack
,然后在这两个地方去处理我们需要在Handler
所处线程要做的事情;而post
可以直接通过匿名内部类,实现起来方便很多
但实际上的实现是一样的,都是通过调用
enqueueMessage
入队消息
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
13、sendEmptyMessageDelayed
与本方法类似的方法还有:
sendMessage
:接受一个Message
对象参数sendMessageDelayed
:除了Message
,还有delayed
字段sendMessageAtTime
:其他方法都是调用该方法,不像其它方法的when
字段是在SystemClock.uptimeMillis()
基础上计算出来,sendMessageAtTime
的when
字段直接就是用方法参数的时间sendEmptyMessage
:接受一个msg.what
参数sendEmptyMessageDelayed
:同上sendEmptyMessageAtTime
:同上sendMessageAtFrontOfQueue
:这是“插队”方法
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
14、sendMessageAtFrontOfQueue
实现插队的方法之一,实现原理如
postAtFrontOfQueue
所说
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
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, 0);
}
15、executeOrSendMessage
针对那些对所处线程没有要求的任务
public final boolean executeOrSendMessage(@NonNull Message msg) {
//如果同一线程
//直接dispatch->handleMessage
if (mLooper == Looper.myLooper()) {
dispatchMessage(msg);
return true;
}
//否则发送消息
return sendMessage(msg);
}
16、enqueueMessage(重要)
除了同步屏障外,其他方法都是通过这个方法进行消息入队
而且Handler造成内存泄漏
的原因也在于此:msg.target = this
在这里将Handler
自己赋值给Message
的target
字段,这样在分发的时候才能找得到对应的对象
也就是说如果msg
是一个延时很久的任务,那可能持有Handler
的Activity
已经被销毁了,但Handler
对象还在Message
中等待被处理,这样就会导致这个对象不能被回收,导致Activity
无法在GC
标记阶段被标记为可回收对象,从而导致内存泄漏
(顺便说一句,BroadcastReceiver
也类似于这样处理,将ReceiverDispatcher
作为参数传给AMS
,但它用了弱引用,不像Handler
这里用了强引用)
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//这里会将自己作为Message的target字段
//所以如果delayed太久的话,很有可能会引起内存泄漏
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
17、BlockingRunnable(重要)
在
runWithScissors
中使用
实现原理:通过postAndWait
将消息发送出去,同时进入wait
状态,等待另一个线程执行完run
方法然后调用notifyAll
唤醒它
问题:(A线程发送给B线程)
- 如果超时了,只是
A
线程这边不再继续阻塞,但实际上这个Runnable
是已经发送出去进入MessageQueue
了,也就是说它始终是会执行的,那这样就会出现消息回调在不可预测的时间点
上- 如果
timeout为0
,在将Runnable
发出去之后,调用quit
方法,quit
是会清空消息队列的,也就是A
永远都无法被唤醒,如果此时A
还持有其他锁,那就可能会造成死锁
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
//这里也就是下面:handler.post(this)运行的代码
//运行的线程在对面线程,如果成功了,则唤醒在等待的锁
@Override
public void run() {
try {
mTask.run();
} finally {
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
//首先用handler将任务发出去,然后进入锁方法阻塞
//也就是等待对面线程完成任务或者超时
//如果mq正在退出,返回false
if (!handler.post(this)) {
return false;
}
//如果超时了,直接返回false
//还没超时,则继续wait
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
四、Message
1、变量
//消息标签
public int what;
//携带参数1 整型
public int arg1;
//携带参数2 整型
public int arg2;
//携带对象
public Object obj;
//这个是用于Messenger跨进程通信
public Messenger replyTo;
//延迟时间
public long when;
//用于加锁的对象
public static final Object sPoolSync = new Object();
//指向回收的Message,其实缓存池是一个链表
private static Message sPool;
//当前消息池数量
private static int sPoolSize = 0;
//缓存池大小
private static final int MAX_POOL_SIZE = 50;
//这个Runnable就是postxxx传进来的Runnable
//这个Runnable的含义是:
//我打算在Handler创建的那个线程(例如主线程)做什么
//那我就将代码写在这里面,最终被执行是在handler的dispatchMessage
/*package*/ Runnable callback;
2、obtain()
从缓存池中取消息,如果没有的话才通过
new
创建
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
3、obtain(Message orig)
根据传入的
Message
去构造从缓存池取出的消息
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
m.workSourceUid = orig.workSourceUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
4、obtain(Handler h)
指定
Message
的target
字段
//指定目标target
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
5、recycleUnchecked
将消息回收至缓存池,可以看到这里是将
what
置为0,所以才会出现我们在分析post
时候所说的
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//只要复用池里面的数量还没超过最大复用数
//就将消息存在链表
//按照新的存在头的位置
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}