一、基础概念
- UI线程
主线程 ActivityThread ,主线程也是Ui线程,应用启动的时候会启动一个ui线程 - Handler
负责发送消息和处理消息。 - Looper
负责消息循环,循环取出 MessageQueue 里面的 Message,并交给相应的 Handler 进行处理。 - MessageQueue
消息队列,用来存放通过Hangdler 发送的消息,按照先进先出的顺序取出消息,内部使用的是单链表结构(优先级链表) - Message
Handler发送和处理的消息个体,有MessageQueue 管理,携带Handler信息和具体消息。
抽象概念具体化:
- Handler:快递员(收件(发送消息)和派件(处理消息))(收发都是统一快递员,只属于同一家快递公司)
- Message:快递包裹(包裹中携带物品,和快递员身份消息)
- MessageQueue:快递分拣中心(将快递按照时间优先级整理好包裹)
- Looper:快递公司(不停地发送消息)
二、Looper
1、先看一下Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
从这个构造方法中可以看到,Looper在创建的时候会创建一个MessageQueue,
2、Looper中有两个比较重要的方法 prepare( ) 和 loop( )
- 方法一:prepare( )
//定义一个ThreadLocal 存放 Looper 每个线程会有唯一的sThreadLocal
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
//准备初始化looper
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//一个线程只有一个Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建一个Looper
sThreadLocal.set(new Looper(quitAllowed));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static @Nullable Looper myLooper() {
//从ThreadLocal中拿出Looper
return sThreadLocal.get();
}
这里需要对ThreadLocal有一定的了解
ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,这样就保证了一个线程对应了一个 Looper,从源码中也可以看出一个线程也只能有一个 Looper,否则就会抛出异常。
- prepareMainLooper() 方法是 系统在 ActivityThread 中调用的。
问题:一个线程中可以有几个Looper?
一个线程只有一个Looper,由于ThreadLocal保证了,一个线程中只能有一个Looper,并且通过if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}可以保证线程中的Looper不会被替换,Looper一旦创建无法修改。
- 方法二:loop() 开启永动机,一直在轮询消息
public static void loop() {
final Looper me = myLooper();
if (me == null) {//如果Loop没有创建,就会抛出异常,prepare() 要先执行。
//所以如果我们在子线程中使用 Handler 则必须手动调用 Looper.prepare() 和 Looper.loop()
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
...
//死循环轮询消息
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) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
....
msg.recycleUnchecked();// 回收消息
}
}
- 通过上面的代码我们可以知道,开启Looper.loop();首先要先获取Looper,获取不到则会报错,所以在子线程中必须手动调用 Looper.prepare() 和 Looper.loop()。 主线程中为啥不用调动呢,那是因为在主线程初始化的时候就已经调用了。
系统在代码中提供了示例代码
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
- 然后就是进入了一个死循环,Message msg = queue.next(); 通过这个方法从 Looper 的 MessageQueue 中去获取 Message。
queue.next();是一个阻塞方法,没有消息会等待。当有消息可以获取到的时候就会调用 msg.target.dispatchMessage(msg);处理消息。
msg.target 指的发送和处理消息的Handler。 这个方法就会调用我们handleMessage(Message msg)这个方法处理消息。
所以上面的问题就可以回答了,Looper 不需要考虑怎么区分 Message 由哪个 Handler 处理,只负责开启消息循环接收消息并处理消息即可。处理完消息后会调用 msg.recycleUnchecked() 来回收消息。
如何停止消息
Looper提供了两个方法来停止消息
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
这两个方法都会调动MessageQueue中的 void quit(boolean safe)
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;//注意,这里会结束循环。。。
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr); //这里会停止消息阻塞
}
}
MessageQueue -->
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;//直接返回null
}
}
Looper --loop();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return; //直接return 结束了循环
}
}
调用Message的quit()方法的时候会调用native方法将 唤醒阻塞方法。mQuitting = true;结束循环
–> MessageQueue 的next() 里面的死循环就会执行if(ptr == 0)return null。 停止掉了死循环。
–> 由于next()返回为null ,所以在Looper 的loop() 中。if(msg==nul) return; 停止掉了loop这个永动机。
Message(消息)
Message就是一个承载消息的的类,
Message 只有一个无参的构造方法,除了构造方法为还可以通过静态的 Obtain()来获取重用对象。
public static final Object sPoolSync = new Object(); //同步锁对象
private static Message sPool; //全局池消息实例 sPool为最近一个可以使用的空消息对象
private static final int MAX_POOL_SIZE = 50;
/**
* 从全局池返回一个新的消息实例,允许我们在许多情况下避免分配新对象。
*/
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();
}
//回收之后的消息会被存储在消息池中,消息池的最大为 MAX_POOL_SIZE = 50
void recycleUnchecked() {
....
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
- 如果当前全局的消息池的Message实例不为空,就会返回第一个消息实例,并且将消息池指针后移。 回收之后的消息也会加入到消息池中。所以在使用Message 的 obtain();方法可以很好的重复利用资源,避免消耗更多的系统资源。当消息数小于50的时候消息可以一直重复利用。
- Message中有一个next属性,可以存储下一个消息对象,也让消息池和MessageQueue中的消息组成了一个单向链表。
Handler
Handler的构造方法中,空构造方法中使用的是
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();//获取当前线程的Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
handler在初始化的时候就已经拿到了当前线程的Looper。前面Looper的代码已经通过ThreadLocal限定了一个线程只有一个Looper,并且拿到了Looper中的MessageQueue。
所以handler在创建的时候就会持有当前线程的Looper和MessageQueue。
Handler 发送消息的方法。
Handler 有多个可以发送消息的方法,最后都是调用了sendMessageAtTime(@NonNull Message msg, long uptimeMillis) 方法
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(
@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
- 这里看一下sendMessageAtTime的调用流程。
- sendMessageAtTime(Handler) -> enqueueMessage(Handler)->enqueueMessage(MesssageQueue)
- 上面的这个流程就将消息发送到了MessageQueue中。在MessageQueue中enqueueMessage 将发过来的消息组成了一个优先级队列,等待loop()取数据。
再来仔细看一下Handler的enqueueMessage()方法
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这个方法主要的关注点就是msg.target = this; message 中已经将Hangler 携带到message中了。所以message发出去之后由谁来接收处理就不会迷路。
还有一个方法需要了解一下。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.e("Handler", "Handler --- > :" + Thread.currentThread().getName());
}
}, 5000);
这样一个写法肯定都比较熟悉,那么这个方法的调用流程是什么呢。
1、Handler里面的
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
//组装好消息,将Runnable 赋给 Message.callback
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
//处理消息的
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {//callback不为空
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//调用run方法。
private static void handleCallback(Message message) {
message.callback.run();//执行run方法。
}
可以看出Runable post之后会发到一个Message 里面去。处理消息的时候如果 callback不为空,就会调用handleCallback 方法,这个方法调用.run() 方法,所以这个方法是运行在Hangler所属的线程中的。不一定是子线程。
Thread 只有在执行了 .start()方法才会在子线程中运行,直接调动.run()不会启动子线程,只会在所属线程运行。
处理消息
处理消息就比较简单了。 直接调用 handleMessage(msg),非常熟悉了。
MessageQueue
MessageQueue就是存储消息的一个队列。持有链条是这样的。 一个线程会有 唯一的一个Looper —>唯一的Looper中会有唯一的messageQueue。
Handler 通过looper-messagequeue 往队列里面添加消息。同一个线程的消息就会放到同一个MessageQueue中,被同线程的Looper循环取出。
- MessageQueue有两个比较重要的方法,分别是,enqueueMessage() 和 next() 他们一个是存消息一个数取消息。
enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
通过上面的代码可以看出,消息在添加到队列的时候就要进行按照时间的排序。形成一个优先级的队列。
还有就是提供给Looper.loop()调用的next()方法,这个方法代码较长不贴出自己去看一下。
总结就是MessageQueue就是将详细按照时间排好队,然后按照先后顺序让loop取出,发给handler处理。
Handler 原理的总结
Handler原理的整个流程总结下来就是:
handler将消息发送到Looper的消息队列中(messageQueue),
messageQueue 将数据按照时间先后排好队,等待Looper.loop()按照先后顺序取出Message
Looper.loop()取出消息之后,调用消息的Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中.
handler 在 handleMessage(msg)方法中处理我们自己的逻辑。
问题
- 1、一个线程有几个Handler?一个线程有几个Looper?
一个线程可以有多个Handler,可以直接new。
一个线程只有一个Looper,由ThreadLocal控制。