下面是一个使用 Handler 在 Android 中实现线程间通信的完整示例,涵盖 后台任务执行、进度更新 和 UI 安全更新 等场景,基于 Java 实现并适配最新实践(如 Looper.getMainLooper()
明确指定主线程)。
📱 示例功能
- 点击按钮启动后台耗时任务(模拟文件下载)。
- 后台任务通过
Handler
发送进度消息到主线程。 - 主线程更新进度条和文本显示。
- 支持取消任务。
完整代码
1. 布局文件 (activity_main.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100" />
<TextView
android:id="@+id/tvProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="0%"
android:textAlignment="center"
android:layout_marginTop="8dp"/>
<Button
android:id="@+id/btnStart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始下载"
android:layout_marginTop="16dp"/>
<Button
android:id="@+id/btnCancel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消下载"
android:layout_marginTop="8dp"/>
</LinearLayout>
2. Activity 代码 (MainActivity.java)
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
private TextView tvProgress;
private Button btnStart, btnCancel;
private Handler mainHandler; // 主线程Handler
private volatile boolean isCancelled = false; // 任务取消标志
// 定义消息类型常量
private static final int MSG_UPDATE_PROGRESS = 1;
private static final int MSG_TASK_COMPLETE = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progressBar);
tvProgress = findViewById(R.id.tvProgress);
btnStart = findViewById(R.id.btnStart);
btnCancel = findViewById(R.id.btnCancel);
// 初始化Handler,绑定主线程Looper
mainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_PROGRESS:
int progress = msg.arg1;
progressBar.setProgress(progress);
tvProgress.setText(progress + "%");
break;
case MSG_TASK_COMPLETE:
tvProgress.setText("下载完成!");
btnStart.setEnabled(true);
break;
}
}
};
// 开始按钮点击事件
btnStart.setOnClickListener(v -> {
isCancelled = false;
btnStart.setEnabled(false);
startDownloadTask(); // 启动后台任务
});
// 取消按钮点击事件
btnCancel.setOnClickListener(v -> {
isCancelled = true;
btnStart.setEnabled(true);
});
}
// 模拟后台下载任务
private void startDownloadTask() {
new Thread(() -> {
for (int progress = 0; progress <= 100; progress++) {
if (isCancelled) break; // 检查取消标志
// 模拟耗时操作(如下载)
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 发送进度消息到主线程
Message msg = mainHandler.obtainMessage();
msg.what = MSG_UPDATE_PROGRESS;
msg.arg1 = progress; // 传递进度值
mainHandler.sendMessage(msg);
}
// 任务完成/取消后更新UI
if (!isCancelled) {
mainHandler.sendEmptyMessage(MSG_TASK_COMPLETE);
}
}).start();
}
}
关键机制解析
主线程 Handler 初始化
mainHandler = new Handler(Looper.getMainLooper()) { ... }
。- 在
handleMessage()
中安全更新 UI(如进度条和文本)。
- 在
后台线程通信
- 后台线程通过
mainHandler.obtainMessage()
创建消息对象,设置what
(消息类型)和arg1
(进度值)。 - 调用
sendMessage()
将消息发送到主线程队列。
- 后台线程通过
任务取消控制
volatile boolean isCancelled
确保多线程可见性。- 后台循环中检查标志位,及时终止任务