基础常识
1、 Android 中只有主线程才能更新UI,Android机制的规定,主要是为了保证UI绘制的流畅,防止并发出问题;
2、 Android 中主线程不允许阻塞超过5s,否则可能会ANR;
3、 Handler机制的核心就是实现线程之间通信;
如何涉及一套线程间通信的机制?
Q1:如何涉及线程间通信?
A1:利用面向对象这个概念,创建一个对象obj,然后在子线程中给obj的一个属性赋值,然后在主线程中,操作obj对象,就可以拿到子线程赋值的结果,完成一次线程通信;
Q2:主线程什么时候去操作obj对象?
A2:因为不知道子线程什么时候给obj赋值,所以会开启一个死循环去观察obj对象中是否有值,有就进行操作;
Q3:死循环不会导致OOM吗,不会ANR吗?
A3:正常的死循环操作必然不行,可以利用Android中的阻塞式死循环,有消息就进行处理,没消息时就会休眠,当有消息来时将其唤醒处理消息;
常用的类及其作用
-
Handler:向消息池中发送各种消息事件以及处理相应的消息;
-
Message:需要传递的消息,可以传递消息;
-
MessageQueue:消息队列,内部使用的数据结构为单链表(便于插入与删除),主要功能为投递消息与取消息;
-
Looper:开启阻塞式死循环,遍历MessageQueue对象,从消息队列中取消息,按分发机制分发给目标处理者;
注意:一个线程中只能存在一个Looper,每个Looper维护一个MessageQueue,Looper是保存在ThreadLocal中的;
Q:为什么一个线程不能有多个循环,多个队列呢?
A:第一,性能问题,一个循环一个队列就能完成的,没必要多个;第二,顺序执行,如果在第一个循环中没有消息,线程就会休眠,这样后面的循环根本执行不到;第三,由于顺序执行,多个队列等于一个队列;
Handler消息机制原理
1、开启循环:每个线程只有一个Looper,用来阻塞式循环,每个Looper对应一个MessgeQueue;无限的循环遍历MessageQueue,如果里边有消息就去处理消息,消息处理完继续循环,没消息时进行休眠,这样就一直循环下去,也是我们程序为什么不会退出的原因;
2、发送消息: Handler创建的时候会根据线程去绑定,拿到线程对应的Looper和MessageQueue,发送消息的过程在其他线程把Message放到MessageQueue当中;MessageQueue按照Message的触发事件先后顺序来插入到队列中;
3、回调消息: Handler发送消息的时候会在发送的Message消息打上tag,当Looper遍历到Message对象,这个时候已经到了Handler的目标线程,Message.tag就拿到了Handler对象,然后就可以回调对应的方法handler.handleMessage();
消息机制源码理解
方式一、使用Handler.sendMessage()
1、创建对象:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
sThreadLocal这样理解,类似一个和线程有关的HasMap,就是为了保证一个线程只有一个Looper对象,也叫容器单例;当多次调用prepare方法的时候,会抛出一个异常,这样也是为了保证Looper只有一个;如果Looper为空时,就会创建一个,并保存在ThreadLocal中;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
// 1. 创建1个消息队列对象(MessageQueue)
// 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)
mRun = true;
mThread = Thread.currentThread();
}
程序启动的时候,ActivityThread在main方法中会自动调用Looper.prepareMainLooper与loop,代码如下:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
// 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
// 方法逻辑类似Looper.prepare()
// 注:prepare():为子线程中创建1个Looper对象
ActivityThread thread = new ActivityThread();
// 2. 创建主线程
Looper.loop();
// 3. 自动开启 消息循环
...
}
创建主线程时,会自动调用ActivityThread的1个静态的main();而main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,同时也会生成其对应的MessageQueue对象;
而子线程的Looper对象则需手动通过Looper.prepare()创建,在子线程若不手动创建Looper对象 则无法生成Handler对象;
public static void loop() {
...
// 1. 获取当前Looper的消息队列
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
// 即loop()执行前必须执行prepare(),从而创建1个Looper实例
final MessageQueue queue = me.mQueue;
// 获取Looper实例中的消息队列对象(MessageQueue)
// 2. 消息循环(通过for循环)
for (;;) {
// 2.1 从消息队列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息队列里的消息
// 若取出的消息为空,则线程阻塞
// 2.2 派发消息到对应的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派发给消息对象msg的target属性
// target属性实际是1个handler对象
// 3. 释放消息占据的资源
msg.recycle();
}
...
}
消息循环,即从消息队列中获取消息、分发消息到Handler;
注意:
1、主线程的消息循环不允许退出,即无限循环;
2、子线程的消息循环允许退出:调用消息队列MessageQueue的quit();
Message next() {
...
// 该参数用于确定消息队列中是否还有消息
// 从而决定消息队列应处于出队消息状态 or 等待状态
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native层,若是 nextPollTimeoutMillis为-1,此时消息队列处于等待状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
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;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
// 下次循环时,消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
}
...
}
public void dispatchMessage(Message msg) {
// 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
// 则执行handleCallback(msg),即回调Runnable对象里复写的run()
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息(即此处需讨论的)
// 则执行handleMessage(msg),即回调复写的handleMessage(msg)
handleMessage(msg);
}
}
分发给对应的Handler的过程:根据出队消息的归属者通过dispatchMessage(msg)进行分发,最终回调复写的handleMessage(Message msg),从而实现消息处理的操作;
注意:
1、msg.callback不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run();
2、msg.callback为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg);
2、创建消息对象
public static Message obtain() {
// Message内部维护了1个Message池,用于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;
}
// 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存
}
// 若池内无消息对象可复用,则还是用关键字new创建
return new Message();
}
3、发送消息
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
/**
* 分析1:sendMessageDelayed(msg, 0)
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* 分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
**/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 1. 获取对应的消息队列对象(MessageQueue)
MessageQueue queue = mQueue;
// 2. 调用了enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
/**
* 分析3:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 将msg.target赋值为this
// 即:把当前的Handler实例对象作为msg的target属性
msg.target = this;
// Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
// 实际上则是将该消息派发给对应的Handler实例
// 2. 调用消息队列的enqueueMessage()
// 即:Handler发送的消息,最终是保存到消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
/**
* 分析4:queue.enqueueMessage(msg, uptimeMillis)
* 定义:属于消息队列类(MessageQueue)的方法
* 作用:入队,即将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
* 采用单链表实现:提高插入消息、删除消息的效率
*/
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 判断消息队列里有无消息
// a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
// 之后,随着Looper对象的无限消息循环
// 不断从消息队列中取出Handler发送的消息 & 分发到对应Handler
// 最终回调Handler.handleMessage()处理消息
Handler发送消息的本质 = 为该消息定义target属性(即本身实例对象) & 将消息入队到绑定线程的消息队列中;
方式二:使用 Handler.post()
1、发送消息到消息队列中
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
// getPostMessage(r) 的源码分析->>分析1
// sendMessageDelayed()的源码分析 ->>分析2
}
/**
* 分析1:getPostMessage(r)
* 作用:将传入的Runable对象封装成1个消息对象
**/
private static Message getPostMessage(Runnable r) {
// 1. 创建1个消息对象(Message)
Message m = Message.obtain();
// 注:创建Message对象可用关键字new 或 Message.obtain()
// 建议:使用Message.obtain()创建,
// 原因:因为Message内部维护了1个Message池,用于Message的复用,使用obtain()直接从池内获取,从而避免使用new重新分配内存
// 2. 将 Runable对象 赋值给消息对象(message)的callback属性
m.callback = r;
// 3. 返回该消息对象
return m;
}
/**
* 分析2:sendMessageDelayed(msg, 0)
* 作用:实际上,从此处开始,则类似方式1 = 将消息入队到消息队列,
* 即 最终是调用MessageQueue.enqueueMessage()
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// 请看分析3
}
/**
* 分析3:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
**/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 1. 获取对应的消息队列对象(MessageQueue)
MessageQueue queue = mQueue;
// 2. 调用了enqueueMessage方法 ->>分析3
return enqueueMessage(queue, msg, uptimeMillis);
}
/**
* 分析4:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 将msg.target赋值为this
// 即 :把 当前的Handler实例对象作为msg的target属性
msg.target = this;
// 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
// 实际上则是将该消息派发给对应的Handler实例
// 2. 调用消息队列的enqueueMessage()
// 即:Handler发送的消息,最终是保存到消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
// 注:实际上从分析2开始,源码 与 sendMessage(Message msg)发送方式相同
注意:
1、相比sendMessage(),post()最大的不同在于,更新的UI操作可直接在重写的run()中定义;
2、Runnable并无创建新线程,而是发送 消息 到消息队列中