Handler主要函数
1.消息队列(MessageQueue) 消息队列是一个先进先出(FIFO)的数据结构(在MessageQuene中使用链表来实现的),用于存储待处理的消息。每个 Handler 关联一个消息队列,这个队列负责存储和处理发送到该 Handler 的消息。
2.消息(Message) 消息是 Handler 处理的基本单元,可以对消息进行封装。每个消息都包含一个标识符(what)、处理消息的目标 Handler(target)、可选的数据(obj)以及处理消息的方法(callback)等信息。消息对象通过消息队列在不同线程间传递。
3.Looper Looper 是一个线程局部的类,用于管理消息队列。一个线程只能有一个 Looper,而主线程默认已经有一个 Looper。在子线程中使用 Handler 之前,你通常需要调用 Looper.prepare()
和 Looper.loop()
来初始化和启动消息循环。
Handler:消息的处理者,handler 负责将需要传递的信息封装成Message,发送给Looper,继而由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler 对象收到该消息后,调用相应的handler 对象的handleMessage()方法对其进行处理。
class Messagequeue
中有一个Message messages
,Message messages
中又有一个Message next
,故Messagequeue
(队列)中形成了一个链表来存储Message
,enqueueMessage用来入队列,如下所示。
msg.when = when;
Message p = mMessages;
boolean needWake;
if(p == null || when == 0 || when < p.when){
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
//取消息
Message next(){
......
Message msg = mMessages;
......
return msg;
......
}
next()函数是在loop()函数中(Looper()函数中的loop()),通过loop()函数来实现取消息
public static void loop(){
......
for(;;){
Message msg = queue.next();
......
}
......
msg.target.dispacthMessage(msg); //Handler target
......
}
调用dispacthMessage()会触发handleMessage(msg)
public void dispacthMessage(Message msg){
if(msg.callback != null){
handleCallback(msg);
}else{
if(mCallback != null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);
}
}
子线程Handler->sendMessage(); queue.enqueueMessage(); sendMessage就是往Messagequeue中添加Message; Messagequeue是用来存储Message的队列(内部用的是链表); handle在looper->loop()函数中有queue.next()会从Messagequeue中取消息; handler->handleMessage(),主线程中取消息。
在主线程中创建 Handler 时,它会自动与主线程的 Looper 关联。在其他线程中使用 Handler 时,你需要显式地将 Handler 与该线程的 Looper 关联。
// 在主线程中创建 Handler
Handler handler = new Handler();
// 在子线程中创建 Handler
Handler handler = new Handler(Looper.getMainLooper());
使用 Handler.sendMessage(Message msg)
方法可以向消息队列发送消息。
Message message = handler.obtainMessage();
message.what = MESSAGE_ID;
message.obj = someObject;
handler.sendMessage(message);
通过重写 Handler.handleMessage(Message msg)
方法,可以处理收到的消息。
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
使用Handler.post()来发送消息
// 步骤1:在主线程中创建Handler实例
private Handler mhandler = new mHandler();
// 步骤2:在工作线程中 发送消息到消息队列中 & 指定操作UI内容
// 需传入1个Runnable对象
mHandler.post(new Runnable() {
@Override
public void run() {
... // 需执行的UI操作
}
});
// 步骤3:开启工作线程(同时启动了Handler)
// 多线程可采用AsyncTask、继承Thread类、实现Runnable
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private TextView tvContent;
String strFromNet;
private Handler mHandler = new Handler(Looper.myLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if(msg.what == 0)
{
String strData = (String) msg.obj;
tvContent.setText(strData);
Toast.makeText(MainActivity.this, "主线程收到消息啦!", Toast.LENGTH_SHORT).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
tvContent = findViewById(R.id.tv_content);
}
public void start(View view){
new Thread(new Runnable() {
@Override
public void run() {
String stringFrommat = getStringFromNet();
Message message = Message.obtain();
message.what = 0;
message.obj = stringFrommat;
mHandler.sendMessage(message);
}
}).start();
Toast.makeText(this, "任务完成!", Toast.LENGTH_SHORT).show();
}
private String getStringFromNet(){
String result = "";
StringBuilder stringBuilder = new StringBuilder();
//模拟一个耗时操作
for(int i = 0; i < 100; i++)
{
stringBuilder.append("字符串" + i);
}
try{
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
result = stringBuilder.toString();
return result;
}
}
首先点击按钮会触发start函数,在start函数中创建了一个子线程,在子线程中创建消息的内容,即在getStringFromNet()中输入字符串,为了看到效果(子线程只执行run()中的代码,外面的代码都在主线程中执行),睡眠了三秒,之后使用sendMessage()将消息发送到Messagequeue中,随后主线程中使用handleMessage()去Messagequeue中取消息,最后将消息显示出来。
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class HandlerActivity extends AppCompatActivity {
private Handler handler = new Handler();
private TextView tvContent;
String str = "";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
tvContent = findViewById(R.id.tv_content);
}
public void start(View view){
new Thread(){
@Override
public void run(){
//处理信息
try{
sleep(3000);
//这里使用Runnable
Runnable runnable=new Runnable() {
@Override
public void run() {
StringBuilder stringBuilder = new StringBuilder();
//模拟一个耗时操作
for(int i = 0; i < 100; i++)
{
stringBuilder.append("字符串" + i);
}
str = stringBuilder.toString();
tvContent.setText(str);
}
};
//这里执行post(Runnable)操作
handler.post(runnable);
}catch (Exception e){
e.printStackTrace();
}
}
}.start();
}
}
/**
* 源码分析: Looper.loop()
* 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
* 特别注意:
* a. 主线程的消息循环不允许退出,即无限循环
* b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
*/
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对象
// ->>分析1
// 3. 释放消息占据的资源
msg.recycle();
}
}
/**
* 分析1:dispatchMessage(msg)
* 定义:属于处理者类(Handler)中的方法
* 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
*/
public void dispatchMessage(Message msg) {
// 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息(即此处需讨论的)
// 则执行handleCallback(msg),即回调Runnable对象里复写的run()->> 分析2
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);
}
}
/**
* 分析2:handleCallback(msg)
**/
private static void handleCallback(Message message) {
message.callback.run();
// Message对象的callback属性 = 传入的Runnable对象
// 即回调Runnable对象里复写的run()
}
loop()里面是一个循环,这样可以不断的获取消息。在没有消息处理的时候Android主线程会进入阻塞,让出cpu时间,等到下一条消息到来的时候(enqueueMessage)会唤醒线程。
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);//阻塞
synchronized (this) {
// Try to retrieve the next message.Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//检查链表里面是否存在异步消息 target==null代表这个消息是个异步消息屏障
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//获取到消息:可能是普通消息 也可能是异步消息
if (msg != null) {
//消息执行时间未到
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//获取到消息
// Got a message.
//把消息从链表中删除
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 {
// No more messages.
//链表里面没有消息,永久阻塞,等待唤醒
nextPollTimeoutMillis = -1;
}
//......省略
mBlocked = true;
continue;//进入下一次循环
}
}
在next方法中,从队列中读取消息时,会先检查是否存在同步屏障。如果存在同步屏障,优先返回队列中的异步消息,并将该异步消息从队列中移除。如果队列中没有同步屏障,则返回队列中的同步消息,并将该同步消息从队列中移除
同步消息中会有一个变量when,这个变量存储的就是时间,messageQueue里面的message会按照链表执行的时间顺序从链表头到链表尾排序,链表链表头的消息会最先执行。
异步消息:异步指该消息会优于其他普通消息执行,在异步消息执行完毕之前其他消息无法执行。通过postSyncBarrier()
来创建一个屏障,通过removeSyncBarrier()
来移除掉屏障。这样通过message.setAsynchronous(true) 设置的异步消息就会优先执行。
/**
* 源码分析:mHandler.sendMessage(msg)
* 定义:属于处理器类(Handler)的方法
* 作用:将消息 发送 到消息队列中(Message ->> MessageQueue)
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
// ->>分析1
}
/**
* 分析1:sendMessageDelayed(msg, 0)
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// ->> 分析2
}
/**
* 分析2: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);
}
/**
* 分析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发送的消息,最终是保存到消息队列->>分析4
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.post(Runnable r)
* 定义:属于处理者类(Handler)中的方法
* 作用:定义UI操作、将Runnable对象封装成消息对象 & 发送 到消息队列中(Message ->> MessageQueue)
* 注:
* a. 相比sendMessage(),post()最大的不同在于,更新的UI操作可直接在重写的run()中定义
* b. 实际上,Runnable并无创建新线程,而是发送 消息 到消息队列中
*/
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)发送方式相同
参考连接:
Android异步通信:手把手带你深入分析 Handler机制源码 - 简书 (jianshu.com)