Handler机制分析
Handler机制分析
1. Handler机制原理
Android系统中Java应用程序是靠消息驱动来工作的,消息驱动工作原理如下:
-
存在一个消息队列,事件源可以往消息队列中投递消息。
-
存在一个消息循环,不断从消息队列中取出消息进行处理。
事件源可以是按键、触摸等物理事件也可以是系统或者应用本身发出的请求消息
工作过程如下:
从图中可以看出:
-
事件源把待处理的消息加入到消息队列中,一般是加入到队尾,一些优先级高的消息也可以加到队头。
-
处理线程不断从队头取出消息并处理。
那么Android是如何实现的呢?
Handler机制就是具体实现,上述工作就是由Handler和Looper实现:
-
Handler类作为辅助类,封装了消息投递和消息处理接口。
-
Looper类,用于封装消息循环,并且有一个消息队列(Message队列,里面存储待处理的Message)。
2. Handler机制部分源码分析
a. Looper类分析
首先分析Looper类,我们通过Android程序入口主线程初始化Looper过程开始分析Looper类。
public static void main(String[] args) {
...
//1. 调用初始化Looper
Looper.prepareMainLooper();
...
//2. 进入消息循环
Looper.loop();
...
}
这里有2个关键调用来初始化主线程Looper,我们先分析1处调用。
第一个调用是Looper.prepareMainLooper()
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) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 创建一个Looper对象设置到调用线程的局部变量中
sThreadLocal.set(new Looper(quitAllowed));
}
关键函数prepare会在调用的线程的局部变量中设置一个Looper对象,这个Looper对象就保存在这个调用线程的TLV中,就是通过ThreadLocal机制巧妙的把Looper和调用线程关联在一起。
这里可以看到一个线程只能调用一次prepare函数给自己设置一个Looper对象,所以每个线程只能有一个Looper对象。
第二个调用是Looper.loop()
public static void loop() {
final Looper me = myLooper();// myLooper返回保存在调用线程TLV中的Looper对象
...
me.mInLoop = true;
...
for (;;) {// 死循环遍历处理每一个消息
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // 获取一个队列头消息
...
try {
// 处理消息,Message对象中有一个target,它是Handler类型
// 调用该消息的Handler,交给他的dispatchMessage函数处理
msg.target.dispatchMessage(msg);
...
} catch (Exception exception) {
...
} finally {
...
}
msg.recycleUnchecked();
}
上述代码分析loop的作用:开启一个死循环读取线程TLV中Lopper对象的消息队列并处理。
通过上述分析可以得到:
-
Looper的prepare函数会将Looper对象和调用线程(也是最终处理线程)绑定。
-
处理线程调用loop函数,处理Looper对象消息队列的消息。
当事件源向这个Looper发送消息的时候,就是把消息加到这个Looper对象的消息队列中,之后该消息将由和Looper绑定的处理线程来处理。
事件源何如把消息添加到处理线程的Looper对象的消息队列中呢?
b. Hander类分析
先看下代码中Handler是如何使用的,应该都不陌生下面这段代码
private Handler mHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
...
super.handleMessage(msg);
}
};
代码中有2个地方需要注意:
- 在new一个Handler对象的时候会传递一个Looper对象,通过分析源码Handler构造函数,即使没有传递一个Looper对象,也会主动通过Looper.myLooper函数获取当前线程TLV中保存的Looper对象。
由此可以知道Handler和Looper对象初始化是有顺序的,Looper对象需要在Handler对象初始化前就初始化完成。
- 需要重载handleMessage方法,具体处理不同消息。
Handler为什么需要Looper对象呢?用来做什么?
前面原理部分和Looper类分析说过Looper封装了消息循环并且有一个消息队列,如果没有Handler我们如何往Looper中的消息队列中插入消息?三步可以做到:
-
调用Looper.myQueue获取到MessageQueue消息队列对象。
-
构造一个Message,写入成员变量,特别是target变量。
-
调用MessageQueue.enqueueMessage将消息插入到消息队列
可以看到自己实现这些操作很麻烦,Handler的出现就是为了简化这些操作,它需要Looper对象的原因是因为它帮我们做了上述复杂操作,Handler提供一系列API帮我们完成创建消息和插入消息队列的工作,例如常用的:
// 查看消息队列中是否有消息码为what的消息
public final boolean hasMessages(int what)
// 从Handler中创建Message,涉及到复用不展开,有兴趣看下源码
public final Message obtainMessage()
// 从消息队列中移除消息码为what的消息
public final void removeMessages(int what)
// 发送消息
public final boolean sendMessage(@NonNull Message msg)
分析下sendMessage做了什么
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// mQueue就是Looper中的消息队列
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, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 把target设置成自己
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 将消息入队列
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到进入一些调用后Handler把Message中的target设置为自己,然后加入到消息队列中。
上面分析了Handler消息投递的代码,那么消息处理是什么样的呢?
分析Looper类的时候在loop方法中死循环获取消息处理时有一个调用msg.target.dispatchMessage(msg),没错是调用Handler.dispatchMessage来处理消息。
public void dispatchMessage(@NonNull Message msg) {
// 如果Message本身有callback,则交给callback来处理
if (msg.callback != null) {
handleCallback(msg);
} else {
// 如果Handler设置了callback,则交给callback来处理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 交给子类来处理
handleMessage(msg);
}
}
可以看到dispatchMessage定义了一套消息处理的优先级机制:
-
如果消息自带callback处理,则交给callback处理。
-
如果Handler设置了全局mCallback处理,则交给mCallback处理。
-
如果1,2都没有设置则交给子类实现的handleMessage处理,也就是上面例子中初始化Handler需要重载的函数。
至此Handler机制就说完了
内容来自网络和书本总结,如果侵权留言联系删除。
浙公网安备 33010602011771号