线程
线程间通信原理:
从上面的流程图可以看出,handler和looper构成了简单的生产者和消费者模式:hander是生产者,looper是消费者.
优点:保证数据产生,消费的顺序(通过MessageQueue,FIFO)无论是生产者还是消费者都只依赖缓存区(handler),生产者和消费者之间不会相互持有,也就不会构成耦合.
Looper
对线程中消息队列(MessageQueue)进行循环获取到Message,然后对Message进行分发处理,在Handler中调用Callback或handleMessage()来处理事件
Thread在默认情况下(主线程除外,主线程在程序启动时就已经创建了Looper对象)并没有与它关联的Looper,所以在handler对象生成之前需要调用Looper.prepare()来创建与线程关联的Looper对象,在线程中任务开始执行时,调用Looper.loop()来循环对应线程中的消息,
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
// 创建looper
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// do something()
}
};
// 循环队列
Looper.loop();
}
App开启动时就调用如下函数生成了sMainLooper对象,它处于主线程中,运行于app的整个生命中
public static void main(String[] args) {
Looper.prepareMainLooper();
Looper.loop(); //开始轮循操作
}
-
static final ThreadLocal sThreadLocal
通过ThreadLocal持有Looper对象保证了线程之间的Looper对象互不影响,即线程并发安全性 -
final MessageQueue mQueue;
需要执行循环的消息队列 -
final Thread mThread;
持有该Looper的Thread对象 -
Looper.prepare(boolean quitAllowed)
quitAllowed:表示是否可以使用quit()
子线程中quitAllowed一般都是默认为true,我们可以控制;但是在主线程中为false.不可更改,它由系统控制
public static void prepareMainLooper() {
prepare(false); // 主线程不可以退出
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {// 每个线程中都只有一个Looper,如果重复调用该函数就会抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
- 构造方法 Looper()
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread(); // 绑定当前的线程
}
初始化Looper时,创建MessageQueue,同时绑定与该Looper对象对应的线程
- loop()
public static void loop() {
final Looper me = myLooper();// 获取当前线程对应的Looper对象
if (me == null) { // 该线程没有初始化Looper
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取指定线程中Looper的消息队列
for (;;) { //这是一个死循环,从消息队列不断的取消息
Message msg = queue.next(); // might block
if (msg == null) {
// 消息队列中没有消息队列已经调用了quit函数.
return;
}
// 调用handler的dispatchMessage()对消息进行处理
msg.target.dispatchMessage(msg);
// 回收消息,将消息内部数据置为空
msg.recycleUnchecked();
}
}
注意:
- loop()执行之前必须确定它所在的线程中已初始化Looper对象
- Looper中的MessageQueue调用quit()后,清空消息队列,Message回收处理
- quit()
- quitSafely()
它们都会调用MessageQueue.quit();
void quit(boolean safe) {
if (safe) {
removeAllFutureMessagesLocked();// 清空MessageQueue消息池中所有的延迟消息(通过postDelay()...发送的消息)并将消息池中所有的非延迟消息派发出去让Handler去处理
} else {
removeAllMessagesLocked();// 清空MessageQueue中的所有消息
}
}
}
quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息,调用quit之后,Looper就不再接收新的消息,因为消息队列已经退出了.
ActivityThread.java
中,在退出程序时会调用主线程中Looper.quit()
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
通过上述介绍,我们可以知道:
- 每个线程Thread都有与之对应的一个Looper对象
- 该Looper对象中含有一个MessageQueue,Looper通过loop()不断循环得到位置的Message,Message通过他的属性target(Hanlder对象)调用dispatchMessage将消息传入到handleMessage()中
- 在创建Handler之前应该先创建对应线程的Looper对象,(Looper中通过ThreadLocal存储looper对象),ThreadLocal可以使不同线程持有不同的Looper对象,即实现了多线程之间数据安全性
ThreadLocal
为每一个使用该变量的线程提供一个变量值的副本,是java中一种较为特殊的线程绑定机制,即:每一个线程都可以独立的改变自己的副本,而不会和其他线程的副本发生冲突.在ThreadLocal中有一个Map,用于存储每一个线程的变量副本.其中,key为当前线程,value为数据,不同的线程的key是不同的,所以它们之间的数据不会发生错误.
MessageQueue
保存线程通信时用到的消息,对消息进行插入,获取,移除回收操作;
我们一般说他是消息队列包含所有的消息,但是实际上它只包含一个Message,而在每Message中有一个Message对象next,就这样每个Message都有一个next指向下一个Message对象,从而形成了一条数据链.
该队列根据任务运行时间排序
- Long mPtr; // MessageQueue对象地址
- Message mMessages:当前消息队列头部消息
…其他的在下面的函数介绍中会介绍到这里就不说了
MessageQueue是通过enqueueMessage()和next()来执行Message的插入和取出
- 构造函数
MessageQueue数据实际上是在native底层初始化的,从代码中就可以看出来
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
// mPtr是从android_os_MessageQueue_nativeInit返回的MessageQueue的地址
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
return 0;// MessageQueue初始化失败
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
- enqueueMessage(Message msg, long when)
将数据插入队列中
boolean enqueueMessage(Message msg, long when) {
// 1.判断消息是否可用
// 2.开始循环MessageQueue中的消息,将Message插入到对应的位置
synchronized (this) {
if (mQuitting) {// 该消息队列已结束循环,Message插入失败,回收message
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//将Message插入到MessageQueue队列的头部
msg.next = p;
mMessages = msg;
needWake = mBlocked;// 如果阻塞,则需要唤醒.
} else {
//通过循环找出msg在队列中的位置(按照时间顺序(从小到大)),然后将Message插入到队列中对应的位置
// 判断是否需要唤醒队列:如果队列阻塞,头部消息是Barrier并且插入的消息是异步消息则有可能需要唤
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
// p.next = msg,判断是否需要唤醒:消息队列中有异步消息并且执行时间在新消息之前,所以不需要唤醒。
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}// 将数据插入到数据链中
msg.next = p;
prev.next = msg;
}
// 如果需要唤醒Looper,调用nativeWake取消阻塞
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
从上面我们可以看出
- MessageQueue中的数据结构为单链表,顺序是Message被执行的时间大小,(从小到大)
- 如果队列头部消息为空或者插入的消息可以立即执行的话,将数据插入到队列头部时
- 否则队列会进行循环,将指定的Message插入到合适位置.
- 消息插入到队列时,可能需要调用nativeWake来唤醒Looper
- next()
```java
Message next() {
//消息队列已不存在
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int nextPollTimeoutMillis = 0; // 阻塞时长
for (;;) {
//阻塞操作,等待唤醒或者等待nextPollTimeoutMillis时长
nativePollOnce(ptr, nextPollTimeoutMillis);// 阻塞操作
synchronized (this) {// 同步操作为了数据安全和准确性
// uptimeMillis用来判断时间(不受系统时间影响).
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 这种情况是队列中当前消息是barrier类型的,寻找队列中下一条的异步消息
// Barrier同步障碍消息(用来阻塞队列的),异步消息不受该消息的影响,
// postSyncBarrier():向MessageQueue中添加同步障碍消息
// removeSyncBarrier():移除消息队列中对应的障碍消息;
do {
// 获取异步或null Message
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) { // 该消息处于阻塞状态
// 设置消息阻塞时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else { // 处于唤醒状态
// 将得到的next Message
return msg;
}
} else {
// 队列中没有消息
nextPollTimeoutMillis = -1;
}
// 结束消息循环,清空消息队列,释放内存
if (mQuitting) {
dispose();
return null;
}
}
}
- public int postSyncBarrier()
- private int postSyncBarrier(long when)
- public void removeSyncBarrier(int token)
Barrier: 它也是一种Message,在next()
函数中就有简单介绍
消息的target为null就表示它是个Barrier,在MessageQueue中,有两种向消息队列中添加消息的函数,一种是enqueueMessage,另一种是enqueueBarrier,而enqueueMessage中如果mst.target为null是直接抛异常的,这个在源码中可以看到.
通过enqueueBarrier往消息队列中插入一个BarrierMessage,队列中消息执行时,在这个Barrier以后的同步消息都会被这个Barrier拦截阻塞住无法执行,直到我们调用removeBarrier移除了这个Barrier,或者异步消息. 因为异步消息不受它的影响,可以继续执行.
-
removeCallbacksAndMessages(Handler h, Object object)
-
removeMessages(Handler h, Runnable r, Object object)
移除消息队列的Message,我们通过调用handler.remove()来调用它们. -
boolean hasMessages()
判断消息队列是否含有指定消息
IdleHandler
原文:Callback interface for discovering when a thread is going to block waiting for more messages
MessageQueue中的message处理完了或者是需要阻塞等待一段时间,这个时候会回调这个接口
作用:就是在线程中的消息处理完或消息处于阻塞时调用该函数.这时,我们就可以做一些我们自己需要的操作了.
queueIdle()
调用该接口时,如果该函数返回false,那么就会把它从消息队列的mIdleHandlers中移除,返回true就会在下次继续回调
-
public void addIdleHandler(@NonNull IdleHandler handler)
向消息队列中添加IdleHandler对象 -
public void removeIdleHandler(@NonNull IdleHandler handler)
从消息队列中移除IdleHandler对象 -
该函数在源码中的调用
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
for (;;) {
synchronized (this) {
if (pendingIdleHandlerCount <= 0) {
// 没有IdleHandler,继续循环
continue;
}
}
// 循环调用MessageQueue中的mIdleHandlers,
for (int i = 0; i < pendingIdleHandlerCount; i++) {
try {
keep = idler.queueIdle();
} catch (Throwable t) {
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
}
}
我们可以看到:在调用next()函数之后,我们先循环获取消息,如果队列中的消息为null或者是处于阻塞时,才会调用上面这段代码来循环mIdleHandlers,如果mIdleHandlers中含有数据,就会执行IdleHandler中的queueIdle()函数,最后判断是否需要从mIdleHandlers中移除IdleHandler对象
在源码ActivityThread中就使用到了IdleHandler ,通过下面代码我们可以更加直观的了解IdleHandler.
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
void unscheduleGcIdler() {
if (mGcIdlerScheduled) {
mGcIdlerScheduled = false;
Looper.myQueue().removeIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
Message
- what: 用来分辨消息内容的标识符
- arg1,arg2:用来存储整形数据,表示消息内容
- obj :用来保存我们需要的对象
- when:发送消息的时间,以毫秒为单位
- data(Bundle):用来存储数据
- callback(Runnable):用来存储需要执行Runnable对象:具体可以查看handler.post(Runnable)和handler.getPostMessage(Runnable r),它不会生成新的线程
- Message sPool:改线程对象池中的头部Message对象
该类中最重要的只有两个函数,就生成(obtain),回收(recycler),
生成
- obtain()
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
它主要是从Message对象池中取出一个Message对象
其他obtain()都是基于该函数,只是指定了Message的一些属性而已
回收
- recycleUnchecked()
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
该函数把Message内的数据置为初始状态,然后存储对象池中
Handler
发送消息
- sendMessage()
- post()
这些函数实际上最后调用的是:handler.enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}// 通过looper获取到messagequeue,然后调用messagequeue.enqueueMessage()将Message插入到队列中
return queue.enqueueMessage(msg, uptimeMillis);
}
接受消息
- Looper.loop()
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
try {
msg.target.dispatchMessage(msg);
}
msg.recycleUnchecked();
}
}
通过Looper.loop不断循环调用MessageQueue.next()获取队列头部的消息Message,因为message包含Handler对象,所以通过message.target.dispatchMessage(msg)来分发消息.
- dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
该函数判断消息中是否含有Runnable,如果有则执行handleCallback(msg);否则,如果handler中含有Callback 接口对象的话,就调用Callback.handleMessage(),handler没有callback对象的话则调用handler.handleMessage()来处理Message
- handleCallback()
private static void handleCallback(Message message) {
message.callback.run();
}
他实际上是回调Runnable的run()函数.
- Callback.handleMessage()
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
这个是我们在创建handler时,需要传入的接口对象,回调时使用
- Handler.handleMessage()
我们在多线程消息接收处理中使用最多的就是这种模式了
消息移除
- removeMessage()
它们最终调用的都是MessageQueue中的removeMessage()和removeCallbacksAndMessages()
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
在MessageQueue中,循环把队列中的全部消息都通过recycleUnchecked()回收到Message的对象回收池中.
- Handler():创建handler对象
他们的具体不同之处可以看源码了解
- createAsync(@NonNull Looper looper)
- createAsync(@NonNull Looper looper, @NonNull Callback callback)
创建一个Handler对象,它和正常Handler对象的区别就是它发布的Message或Runnable不会受到同步栅栏消息的阻塞
同步栅栏消息只阻塞同步消息.
总结
- 一个线程有几个handler,一个线程有几个 Looper?如何保证?
每个线程有多个handler,一个looper,每个looper中都有一个MessageQueue, 通过使用ThreadLocal保证每个线程中只有唯一的一个Looper
变量时共享的 MessageQueue不能说是属于哪个线程,函数是有线程区别的 - Handler内存泄漏原因? 为什么其他的内部类没有说过有这个问题?
内部类持有外部类的对象的引用,如果message在外部类对象销毁时,还没有到执行时间,即MessageQueue还存在任务需要执行,这时根据GC可达性算法,外部类对象无法销毁.
如果是静态内部类,这时使用static修饰,他属于类的属性,不属于对象的,在对象销毁时,不会持有外部对象的引用,就不会造成内存泄漏
Handler handler = new Handler() {
public void handleMessage(Message msg) {
MainActivity.this.test();// 对象调用
}
};
Handler handler = new Handler() {
public void handleMessage(Message msg) {
MainActivity.test();// 类调用
}
};
- 为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
主线程在程序启动时就生成了一个MainLooper()
子线程在new handler时:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
// 创建looper
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// do something()
}
};
// 循环队列
Looper.loop();
}
- 子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
子线程中维护的looper在无消息时调用quit,可以结束循环.
loop()是一个死循环,想要退出,必须msg == null,
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.recycleUnchecked();
}
}
只有在调用quit退出的时候才会返回null
Message next() {
for (;;) {
synchronized (this) {
if (mQuitting) {
dispose();
return null;
}
}
}
}
void quit(boolean safe) {
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
}
}
所以使用quit()唤醒队列,执行loop()退出循环,子线程的looper不再执行了.
消息入队:根据时间排序,当队列满的时候,阻塞,直到用户通过next取出消息,当next被调用的时候通知MQ可以进行消息的入队
消息出队:由Looer.loop进行循环,对queue进行轮询操作,当消息达到执行时间就取出,但MQ为空的时候,队列阻塞,等待消息队列调用enqueue的时候,通知队列可以取出消息,停止阻塞.
handler
没有使用多线程中阻塞队列 BlockQueue,因为主线程(系统)也在使用,如果使用阻塞队列BlockQueue设置上限的话,系统可能会发生卡顿
looper循环阻塞:
- 执行时间阻塞(没有到执行时间), nativePollOnce(long ptr, int timeoutMillis)执行阻塞操作,timeoutMillis为-1表示无限等待,直到有事件发生为止,如果为0,无需等待立即返回;
nativePollOnce(long ptr, int timeoutMillis) timeoutMillis不为-1,时间一到,自动唤醒
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);// 循环进入阻塞状态,等待执行时间到达后唤醒
synchronized (this) {
if (msg != null) {
if (now < msg.when) {
// 消息不为空,并且没有到执行时间,nextPollTimeoutMillis 不为-1
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
}
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
}
}
2.MQ为空,执行阻塞,等待唤醒;
插入消息时,手动唤醒
Message next() {
if (ptr == 0) { // mPtr==0,表示中断循环,
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
if (msg != null) {
} else {
// 无消息,timeoutMillis为-1表示无限等待,直到有事件发生为止
nextPollTimeoutMillis = -1;
}
}
}
}
// mPtr==0
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
// 唤醒
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
boolean needWake;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
// mPtr != 0 循环没有中断,进行唤醒操作.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
- 既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?取消息呢?
锁synchronized: synchronized内置锁,它是由jvm自动完成的,插入和取都需要锁,因为取的时候,可能正在插入.它是锁的对象,因为MessageQueue 每个线程中只有一个Looper,每个Looper又只有一个MQ.