一、Handler机制中基本概念
-
消息队列(Message Queue):Handler 对象关联着一个消息队列,类似于一个邮箱,用来存放待处理的消息。
-
Looper 循环:每个线程中都有一个 Looper 对象,负责循环地从消息队列中取出消息,并将其发送给相应的 Handler。
-
消息处理:当 Handler 发送消息时,消息被加入到关联的消息队列中。Looper 循环不断地从消息队列中取出消息,然后将其分发给对应的 Handler,并调用 Handler 的 handleMessage() 方法来处理消息。
-
处理器Hnadler:处理消息。
二、使用Handler机制目的
在Android中,主线程(UI线程)不能进行耗时操作,否则会导致界面卡顿。通过Handler,可以将耗时操作放到后台线程中执行,并在执行完成后,通过Handler将结果返回到主线程更新UI。也可以进行不同线程间通信。
三、实例
在主线程中更新 UI 的文本框内容,但是耗时的任务需要在后台线程中执行。这时就可以使用 Handler 机制来实现。
// 创建一个新线程,用于执行耗时任务
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
// 模拟耗时操作
try {
Thread.sleep(3000); // 模拟耗时操作,这里休眠3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 创建一个消息对象,用于通知主线程更新 UI
Message message = new Message();
message.what = UPDATE_UI;
message.obj = "耗时任务执行完成";
// 发送消息到主线程的消息队列中
handler.sendMessage(message);
}
});
backgroundThread.start();
// 在主线程中创建一个 Handler 对象,用于处理消息
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 根据消息类型进行相应的处理
if (msg.what == UPDATE_UI) {
// 更新 UI 中的文本框内容
textView.setText((String) msg.obj);
}
}
};
在上面的例子中,我们首先创建了一个新的线程(backgroundThread),用于执行耗时任务。在任务执行完成后,我们创建了一个消息对象(message),并通过 Handler 发送到主线程的消息队列中。主线程中的 Handler 负责处理收到的消息,根据消息类型进行相应的处理,例如更新 UI 中的文本框内容。
四、补充说明
每个 Handler 都会与一个特定的消息队列相关联,并且可以通过该消息队列来接收和处理消息。这意味着,在一个线程中可以有多个 Handler 对象,它们可以处理来自消息队列的消息,执行各种任务。多个子线程的Handler都可以向主线程的Handler发送消息,这些消息都会存放在主线程的消息队列中。当主线程的 Looper 循环从消息队列中获取消息时,它会依次处理这些消息,并调用相应的 Handler 来处理消息。主线程的 Handler 必须在主线程中创建,并关联主线程的 Looper 对象,以确保能够正确地处理主线程的消息队列中的消息。
五、实际开发中的一个简单应用
需要弹出连续两个Toast消息时,后一个会把前一个覆盖,可以使用Handler解决。
handler.post(() -> {
Toast.makeText(context, "Text1", Toast.LENGTH_LONG).show();
handler.postDelayed(() -> Toast.makeText(context, "Text2", Toast.LENGTH_LONG).show(), Toast.LENGTH_SHORT + 500);
});
语法解析:使用post()方法和Lambda表达式,将一个Runable对象投递给与此handler关联的消息队列,等待消息队列中的消息被 Looper 循环取出并交给 Handler 处理,然后执行其中操作。Runable对象中定义了两个Toast操作,并使用了postDelayed()方法将第二个Toast延迟执行,延迟时间为Toast.LENGTH_SHORT + 500ms。
整个代码块的执行逻辑是:先将一个任务(显示 Text1" 的 Toast)投递到消息队列中,然后在 Toast.LENGTH_SHORT + 500
毫秒后,再将另一个任务(显示 "Text2" 的 Toast)投递到消息队列中。这两个任务会在消息队列中依次执行,而不是并行执行。