一、快速了解Handler
作为一个初学Android的小白,在代码中看到Handler时难免会一头雾水,不知道Handler是什么、有什么作用。本章通过回答What、Why、How,来帮助读者快速了解Handler。
1.1 场景
先来看一下这么个简单场景:页面中有一个Button和一个TextView,点击Button后开始下载文件,下载完成后在TextView显示下载开始时间和下载完成时间。
简单实现
界面如下:

代码如下:
public class MainActivity extends AppCompatActivity {
TextView mTvInfo;
Button mBtnDownload;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvInfo = findViewById(R.id.tv_info);
mTvInfo.setText("点击下方按钮开始下载");
mBtnDownload = findViewById(R.id.btn_download);
mBtnDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String beginInfo = "下载开始 " + getCurrentTime();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String finishInfo = "下载完成 " + getCurrentTime();
mTvInfo.setText(beginInfo + "n" + finishInfo);
}
});
}
public String getCurrentTime() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
Date date = new Date();
return dateFormat.format(date);
}
}
上面这段代码是有问题的,按下按钮后,整个界面会卡住,无法操作。Android开发中,在主线程(UI线程)上休眠,会导致应用无响应(ANR)。
因此,在Android开发中,耗时操作(如网络请求、数据库操作、复杂计算等)应该在后台线程上执行,以避免阻塞主线程,这可以防止应用出现界面卡顿或ANR的问题。
将下载任务放在后台线程执行
既然这样,就在点击按钮后开启一个子线程执行下载任务吧~
public class MainActivity extends AppCompatActivity {
TextView mTvInfo;
Button mBtnDownload;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvInfo = findViewById(R.id.tv_info);
mTvInfo.setText("点击下方按钮开始下载");
mBtnDownload = findViewById(R.id.btn_download);
mBtnDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Thread downloadThread = new Thread(){
@Override
public void run() {
String beginInfo = "下载开始 " + getCurrentTime();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String finishInfo = "下载完成 " + getCurrentTime();
mTvInfo.setText(beginInfo + "n" + finishInfo);
}
};
downloadThread.start();
}
});
}
}
这样就可以正常运行了吗?
很抱歉,5s过后,程序崩溃了

报错android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. ,这个错误信息表示你试图在一个非UI线程(也就是不是创建视图层次结构的原始线程)中直接操作UI组件。
在Android中,UI操作(如修改视图属性、添加/移除视图等)必须要在主线程(也称为UI线程)中进行。如果你在子线程中进行这些操作,就会抛出这个异常。
也就是说,在downloadThread里不能执行setText()修改文本框内容,必须回到主线程才行。所以,需要通过一种方式,实现在后台进行耗时操作,操作结束后回到主线程修改UI。
这时,Handler就呼之欲出了
1.2 What:Handler是什么
Handler 是 Android 中用于线程间通信的重要机制,用于主线程(UI 线程)和工作线程之间传递消息和执行任务。
Handler 允许开发者从后台线程发送消息到主线程,或者在主线程上延时执行某些任务。
Handler 通常与 MessageQueue 和 Looper 一起工作,形成一个完整的异步消息处理机制。
- MessageQueue:存储待处理的消息队列。
- Looper:从
MessageQueue中取出消息并分发给对应的Handler处理。 - Message:包含数据的对象,可以通过
Handler发送并在目标Handler中处理。
1.3 Why:为什么要用Handler
在 Android 中,开发者不能直接从子线程中更新 UI,所有 UI 操作必须在主线程(UI线程)上执行。如果子线程需要更新 UI 或与主线程通信,Handler 提供了一个安全的方式将任务传递到主线程执行。同时,Handler 也可以延时执行任务或周期性执行任务。
- 跨线程通信: 在多线程环境中,用于线程间的消息传递。
- UI 更新: 从子线程传递任务到主线程,更新 UI。
- 定时任务: 实现定时任务和延时任务。
1.4 How:怎么使用Handler
创建 Handler 来处理消息
为主线创建一个handler,重写handlMessage方法,用于处理消息。程序执行耗时任务时,开启一个子线程,在任务执行完成后,子线程通过handler.sendMessage(message)将消息发回主线程的消息队列,消息最终会在主线程中被handlerMessage处理,可以实现修改UI。
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
textView.setText("Message Received");
}
}
};
new Thread(() -> {
Message message = handler.obtainMessage();
message.what = 1;
handler.sendMessage(message);
}).start();
使用 Runnable 发送任务
post() 方法直接接受一个 Runnable 对象,当 Runnable 被执行时,它会在主线程中运行。
使用 Handler 的 post 方法可以简化代码,直接将一个 Runnable 对象排队,等待主线程处理,不需要管理 Message 的创建和发送。
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("Task Executed");
}
});
延时执行任务
postDelayed方法可以延时执行任务,例如打开APP时会在欢迎界面停留3s,然后进入登录界面,就可以在欢迎界面里使用postDelayd,3s后跳转到登录界面。
handler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 3000);
在后台线程中使用 Handler
如果你需要在后台线程中使用 Handler,你需要为该线程准备一个 Looper:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler backgroundHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
String data = (String) msg.obj;
Log.d("BackgroundThread", "Received: " + data);
break;
default:
super.handleMessage(msg);
}
}
};
Looper.loop();
}
}).start();
Message message = Message.obtain();
message.what = 1;
message.obj = "Data from Main Thread";
backgroundHandler.sendMessage(message);
1.5 回到开始的场景
通过以上的介绍,大家应该对Handler有了初步的了解。现在回到最开始的场景,我们使用Handler来实现这个Demo,代码如下:
public class MainActivity extends AppCompatActivity {
TextView mTvInfo;
Button mBtnDownload;
Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
mTvInfo = findViewById(R.id.tv_info);
mTvInfo.setText("点击下方按钮开始下载");
mBtnDownload = findViewById(R.id.btn_download);
mBtnDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Thread downloadThread = new Thread(){
@Override
public void run() {
String beginInfo = "下载开始 " + getCurrentTime();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String finishInfo = "下载完成 " + getCurrentTime();
mHandler.post(new Runnable() {
@Override
public void run() {
mTvInfo.setText(beginInfo + "n" + finishInfo);
}
});
}
};
downloadThread.start();
}
});
}
}
现在这个Demo就可以模拟在不阻塞主线程的情况下,执行耗时的下载任务了
二、继续深挖Handler
第一章中介绍了与Handler相关的另外几个概念:MessageQueue,Looper,Message。
通过下面这张图来形象地理解一下这几个类:
- Message:货物,在子线程(主线程也行)中创建后,通过
Handler放置到传送带上。 - MessageQueue:传送带,货物可以在上面存放和取出。
- Looper:电机,驱动传动带前进,依次取出最前面的货物,分发给对应的
Handler处理。

从图中可以看出,Handler是主线程和子线程之间的媒介,实现线程间的消息传递和任务执行。
2.1 Handler
首先对于Handler,我们需要探究两个问题:
Handler如何传递消息Handler如何执行任务
先来看一下Handler的是怎么创建的
Handler的创建
大部分开发者使用的应该都是Handler的无参构造函数,而在Android 11中Handler的无参构造函数已经被标记为废弃的了,如下图所示,删除线表示废弃的方法。

Google 官方更推荐的做法是通过显式传入 Looper 对象来完成初始化,而非隐式使用当前线程关联的 Looper。Looper对象可以有两种方式:
Looper.getMainLooper()Looper.myLooper()

具体细节放在后面介绍Looper时展开说明,这里我们只要知道传入Looper对象的目的是:
将Handler与执行任务的线程绑定
结合本章开始的那幅图,可以形象地理解为要将Handler与一条传送带绑定,指定Handler处理这条传动带上依次传送的货物。即mQueue = looper.mQueue;。
@UnsupportedAppUsage
final Looper mLooper;
final MessageQueue mQueue;
@UnsupportedAppUsage
final Callback mCallback;
final boolean mAsynchronous;
@UnsupportedAppUsage
IMessenger mMessenger;
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
初步了解了Handler的创建,接下来看一下Handler如何发送消息。
Handler如何发送消息
还是看一下本章开始的示意图,在子线程生产了一个货物,需要投递到对应的传送带上,这个就是发送消息的过程。
Handler中发送消息的方法有很多个,大部分最终调用的都是sendMessageAtTime()方法。uptimeMillis即消息具体要执行的时间戳,如果该时间戳比当前时间大,那么就意味着要执行的是延迟任务。sendMessageAtTime()会调用enqueueMessage()将消息投递到消息队列里(也就是将货物放到传送带上),注意下面代码的第16行msg.target = this;,target指向了Handler对象自己,也就是说,Handler自己投递的消息,最终要由自己来处理。
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
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) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
一般发送消息主要会用两个方法:
二者的区别是,sendMessage()发送消息需要显式创建Message对象;post()发送消息不需要显式创建Message对象,但是需要实现任务的执行逻辑。
new Thread(new Runnable() {
@Override
public void run() {
downloadFile();
Message message = handler.obtainMessage(1);
handler.sendMessage(message);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
downloadFile();
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("下载完成");
}
});
}
}).start();
那么,Hanlder如何处理消息?
Handle如何执行任务
传动带上的货物被取出时,如果货物自带了说明书,就按照说明书进行处理,否则就按照提前制定的规则,根据货物类型进行相应的处理
消息队列里的消息会通过Handler的dispatchMessage()方法进行分发,进行相应的处理:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public void handleMessage(@NonNull Message msg) {
}
- 如果该
Message是通过post(Runnable)等方法进行发送的,那么就直接回调该Runnable对象 - 如果在初始化
Handler时传入了Callback对象,则优先交由其处理,如果Callback的handleMessage方法返回了 true,则结束流程 - 调用
Handler的handleMessage方法来处理消息,外部通过重写该方法来定义业务逻辑
一、 通过Runnable发送的消息,实际上也会被封装成Message,最后执行在Runnable中重写的run()方法。
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
二、 Handler中有一个Callback对象,它的注释 “Callback interface you can use when instantiating a Handler to avoid having to implement your own subclass of Handler”的意思是当你创建一个 Handler 实例时,你可以使用回调接口(callback interface)来避免自己实现 Handler 的子类。
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
Handler 类有一个构造函数,允许传入一个 Callback 接口的实例。这个接口只有一个方法 handleMessage,你需要实现这个方法来处理消息。代码示例如下:
public class MyActivity extends AppCompatActivity {
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 1:
String data = (String) msg.obj;
Log.d("MyActivity", "Received: " + data);
break;
default:
return false;
}
return true;
}
});
}
}
三、 在创建 Handler 时,需要重写 handleMessage() 方法,通过 sendMessage() 发送消息时,会指定消息的类型 msg.what,在 handleMessage() 里就根据不同的消息类型,执行不同的处理:
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_TYPE_1) {
} else if(msg.what == MESSAGE_TYPE_2){
}
}
};
2.2 Message
Message 是用来在不同线程之间传递信息的载体,下面介绍一下基本属性和创建方式。
基本属性
Message 对象是用来在线程之间传递数据的载体。一个 Message 对象包含了一些基本的属性,这些属性定义了消息的内容、目标处理器以及其他一些元数据。以下是 Message 类的一些基本属性:
- what:
- 类型:
int - 描述:这个属性通常用来标识消息的类型或者命令。在
Handler的handleMessage方法中,你可以通过检查msg.what的值来决定如何处理不同的消息。 - 示例:假设在一个应用中,有下载图片和下载文件两种异步任务,当任务完成后通过 Handler 发送消息。可以定义
what值,如public static final int DOWNLOAD_IMAGE_COMPLETE = 1;和public static final int DOWNLOAD_FILE_COMPLETE = 2;,在handleMessage方法中就可以根据what的值来判断是图片下载完成还是文件下载完成,进而进行相应的 UI 更新操作。
- 类型:
- arg1 和 arg2:
- 类型:
int - 描述:这两个属性可以用来传递整型数据。它们通常用于传递简单的参数或者状态码。
- 示例:在一个图片加载的场景中,
arg1可以用来表示图片的加载进度(0 – 100),arg2可以表示图片的版本号或者当前加载的图片在一组图片中的序号。
- 类型:
- obj:
- 类型:
Object - 描述:这个属性可以用来传递任何类型的对象。它允许你将任意数据传递给
Handler,但要注意,传递的对象不应包含对当前线程上下文的引用,以避免潜在的内存泄漏。 - 示例:如果从网络上下载了一个包含用户信息(姓名、年龄、性别等)的
User对象,当下载完成后,可以将这个User对象设置为obj属性,然后通过 Handler 发送消息到 UI 线程,在 UI 线程的handleMessage方法中就可以获取这个User对象并进行显示
- 类型:
- target:
- 类型:
Handler - 描述:这个属性定义了处理这个消息的
Handler实例。当一个消息被发送时,target指定了消息应该被哪个Handler的handleMessage方法处理。
- 类型:
- callback:
- 类型:Runnable
- 描述:这个属性允许你指定一个
Runnable对象,该对象将在消息处理时被执行。如果callback不为空,Handler在处理消息时会优先调用callback.run()而不是handleMessage方法。
- data:
- 类型:
Bundle - 描述:这个属性可以用来传递一组键值对,类似于
Intent中的extras。Bundle是一个映射,允许你传递更复杂的数据集合。
- 类型:
- replyTo:
- 类型:
Messenger - 描述:这个属性用于跨进程通信(IPC)时指定回复的
Messenger。在进程间传递消息时,replyTo用来指定接收回复消息的Handler。
- 类型:
- when:
- 类型:
long - 描述:这个字段用于记录消息的发送时间或计划执行时间(取决于消息的发送方式)。
Handler机制会按照消息的when属性值来对消息进行排序和调度。消息队列中的消息会按照时间戳的先后顺序被取出并处理。如果一个消息的when时间戳比当前时间晚,那么它会在到达指定时间后才被处理。
- 类型:
- next:
- 类型:
Message - 描述:在
Message的内部实现中,next字段用于将多个Message对象链接成一个链表,以支持消息对象的复用和缓存。这个字段对开发者来说是透明的,不需要在常规的消息处理中直接操作。
- 类型:
创建方式
在 Android 的 Handler 机制中,Message 有以下几种常见的创建方式:
使用无参构造函数直接创建
这是最直接也是最简单的方式,通过Message类的无参构造函数来创建一个新的 Message 对象。这种方式每次调用都会在内存中创建一个新的 Message 实例,如果频繁使用,可能会导致内存压力增大。
使用 Message.obtain () 方法
这是官方推荐的方式,因为它可以复用之前已经创建但不再使用的 Message 对象,从而减少内存分配和垃圾回收的开销。Message.obtain()方法及其重载版本会从一个全局的 Message 缓存池中获取一个可用的 Message 对象,如果缓存池为空,则新创建一个 Message 对象。
在obtain()方法的内部实现中,它首先会检查一个静态的 Message 对象链表(缓存池)是否非空。如果非空,它会从链表的头部取出一个 Message 对象,将其 next 指针置为null,表示该对象已被取出使用,并将缓存池中的对象数量减一。如果链表为空,则直接通过new Message()创建一个新的 Message 对象。
使用 Handler.obtainMessage () 方法
通过关联的 Handler 来获取一个 Message 对象。这个方法实际上也是调用了 Message.obtain () 方法,但它会自动设置 Message 的 target 属性为调用这个方法的 Handler,确保消息能够由正确的 Handler 处理。
2.3 MessageQueue
MessageQueue就像一个传送带,Message通过传送带存放和取出。
数据结构
MessageQueue 本质上是一个按照时间顺序排列的单链表结构。每个节点代表一个 Message 对象。消息按照它们的when属性值(即执行时间戳)进行排序,时间戳小的消息排在前面,时间戳大的消息排在后面。这样可以保证 Handler 在从消息队列中取出消息时,总是先取出时间戳最小的、也就是应该最先被处理的消息。
使用方式
消息入队
当一个新的 Message 被创建并准备发送到特定的 Handler 时,实际上是将这个 Message 添加到与之关联的 Handler 的 MessageQueue 中。这个过程通常是通过调用Handler.sendMessage(Message msg)方法来实现的。在这个方法内部,会将消息的target属性设置为当前的 Handler,并将消息按照时间顺序插入到消息队列中,调用的是 MessageQueue 的 enqueueMessage()方法:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
···
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
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;
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;
}
消息出队
MessageQueue 中的消息会不断被取出进行处理。Handler 的handleMessage(Message msg)方法会被自动调用,传入从消息队列中取出的 Message 对象。在这个方法中,可以根据消息的what、arg1、arg2、obj等属性来判断消息的类型,并执行相应的操作。
MessageQueue 中消息的不断取出是通过next()实现的。
@UnsupportedAppUsage
Message next() {
···
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
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表示无限时间,直到有事件发生为止,0表示不阻塞。当消息队列为空时,Looper.loop() 会阻塞当前线程,但这种阻塞是可中断的,并且不会导致线程永久挂起。一旦有新的消息到达或 Looper.quit() 被调用,Looper.loop() 会立即返回,从而使线程继续执行。
消息屏障
Android 系统为了保证某些高优先级的 Message(异步消息) 能够被尽快执行,采用了一种消息屏障(Barrier)机制。其大致流程是:先发送一个屏障消息到 MessageQueue 中,当 MessageQueue 遍历到该屏障消息时,就会判断当前队列中是否存在异步消息,有的话则先跳过同步消息(开发者主动发送的都属于同步消息),优先执行异步消息。这种机制就会使得在异步消息被执行完之前,同步消息都不会得到处理。MessageQueue 在获取队头消息的时候,如果判断到队头消息的 target 对象为 null 的话,就知道该 Message 属于屏障消息,那么就会再向后遍历找到第一条异步消息优先进行处理,每次调用 next() 方法时都会重复此步骤知道所有异步消息均被处理完。
我们可以通过调用 Message 对象的 setAsynchronous(boolean async) 方法来主动发送异步消息。而如果想要主动发送屏障消息的话,就需要通过反射来调用 MessageQueue 的 postSyncBarrier() 方法了,该方法被系统隐藏起来了
屏障消息的作用就是可以确保高优先级的异步消息可以优先被处理,ViewRootImpl 就通过该机制来使得 View 的绘制流程可以优先被处理
2.4 Looper
每个
Handler都需要绑定一个Looper对象,每一个Looper对象都有一个MessageQueue。Looper就像是电机,在Looper中开启循环,就可以驱动传送带MessageQueue传动,不断取出最前面的Message执行相应任务。
Looper初始化
Looper如何与MessageQueue绑定
创建Looper对象时,会为其创建一个 MessageQueue 对象,
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper对象何时创建
可以看到Looper的构造函数是私有的,在 Looper.prepare() 里调用
static final ThreadLocal sThreadLocal = new ThreadLocal();
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));
}
其中,ThreadLocal 的作用是为每一个线程设置自己的 Looper 对象,起到线程隔离的作用,每个线程只能读取和修改自己的 Looper 对象。
prepare()何时执行
主线程和子线程在 Looper 的使用上有些区别。在子线程中,我们需要手动调用 Looper.prepare(),为子线程绑定一个 Looper 对象:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
Looper.loop();
}
}).start();
主线程中的 Looper.prepare() 是由系统自动调用的,通常在应用启动时,在进入 ActivityThread.main() 方法后,系统会为主线程创建 Looper 并启动消息循环:
public static void main(String[] args) {
···
Looper.prepareMainLooper();
···
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到第3行 Looper.prepareMainLooper(),这个就是初始化主线程 Looper 的方法,我们再点进去看这个方法:
private static Looper sMainLooper;
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 Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
其中,sMainLooper 表示专门用来表示主线程的 Looper,是一个静态对象,可以通过 getMainLooper获取,作为 Handler有参构造函数的入参,将 Hanlder 与主线程绑定。
Looper循环
loop()开启消息循环
prepare() 和 loop() 是成对出现的。loop() 会开启消息循环,不断从消息队列中取出消息进行分发:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
......
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();
if (msg == null) {
return false;
}
try {
msg.target.dispatchMessage(msg);
...
}
...
msg.recycleUnchecked();
return true;
}
第17行me.mQueue.next()会不断取出消息队列中的消息,通过第24行msg.target.dispatchMessage(msg) 进行分发。
Looper退出
quit()方法- 功能:
quit()方法用于直接退出Looper的消息循环。当调用此方法时,Looper会立即停止处理消息队列中的消息,并尝试退出循环。 - 行为:调用
quit()后,Looper会检查消息队列,如果队列为空或者Looper正在等待新消息(即处于阻塞状态),则Looper会立即退出循环。如果队列中还有未处理的消息,这些消息将被丢弃,Looper不会等待它们处理完毕。 - 使用场景:适用于那些不再需要处理任何消息,且可以立即安全退出的场景。
- 功能:
quitSafely()方法- 功能:
quitSafely()方法用于安全地退出Looper的消息循环。与quit()不同,quitSafely()会等待消息队列中所有已存在的消息都被处理完毕后,再退出循环。 - 行为:调用
quitSafely()后,Looper会继续处理消息队列中的消息,直到队列为空。一旦所有消息都被处理,Looper就会退出循环。 - 使用场景:适用于那些需要确保所有已发送的消息都被处理完毕,再安全退出的场景。
- 功能:
public final class Looper {
...
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
...
}
public final class MessageQueue {
...
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();
}
nativeWake(mPtr);
}
}
...
}
三、参考资料
[1] 一文读懂Handler机制
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22507,转载请注明出处。


评论0