Handler + postDelayed() (最常用、最灵活)
这是 Android 开发中最基础也是最推荐的方式之一,尤其适用于需要在主线程(UI 线程)上更新界面的场景。

核心思想:
Handler 是 Android 中用来处理消息和延迟消息的机制,你可以把它想象成一个“任务调度员”。postDelayed() 方法就是告诉这个调度员:“请帮我安排这个任务,并在指定的延迟时间后执行它”。
关键参数:
-
Runnable r(必需参数)- 类型:
java.lang.Runnable - 描述: 这是你想要在延迟后执行的具体任务,它是一个代码块,通常包含你要执行的逻辑,例如更新 UI、执行某个计算等。
- 示例:
new Runnable() { @Override public void run() { // 这里是你要执行的任务,例如更新 TextView // textView.setText("时间到!"); } }
- 类型:
-
long delayMillis(必需参数)- 类型:
long - 描述: 延迟的时间,单位是 毫秒,延迟 1 秒就传入
1000L。 - 示例:
1000(表示 1 秒后执行)
- 类型:
完整示例代码:
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler(Looper.getMainLooper()); // 明确使用主线程的 Handler
private TextView textView;
private boolean isTimerRunning = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
Button startButton = findViewById(R.id.start_button);
Button stopButton = findViewById(R.id.stop_button);
startButton.setOnClickListener(v -> startTimer());
stopButton.setOnClickListener(v -> stopTimer());
}
private void startTimer() {
if (isTimerRunning) return;
isTimerRunning = true;
// 1. 定义任务
Runnable timerTask = new Runnable() {
@Override
public void run() {
// 更新 UI
runOnUiThread(() -> { // 虽然Handler在主线程,但为了代码健壮性,可以加上
textView.setText("当前时间: " + System.currentTimeMillis());
});
// 2. 任务执行完后,再次安排自己执行,实现循环
if (isTimerRunning) {
handler.postDelayed(this, 1000); // 延迟1秒再次执行
}
}
};
// 3. 第一次执行任务
handler.postDelayed(timerTask, 1000);
}
private void stopTimer() {
isTimerRunning = false;
// 移除所有由这个 handler 发送的消息和回调
handler.removeCallbacksAndMessages(null);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 在 Activity 销毁时,一定要停止 Handler,防止内存泄漏
handler.removeCallbacksAndMessages(null);
}
}
优点:

- 与 Android 消息机制完美集成,非常灵活。
- 可以轻松地在主线程执行 UI 更新。
- 配合
removeCallbacks()可以方便地取消定时任务。
缺点:
- 需要手动管理任务的循环和取消。
- 如果忘记在
onDestroy或适当的生命周期中移除回调,可能会导致 内存泄漏。
java.util.Timer 和 TimerTask (传统方式)
这是 Java 标准库提供的定时器,不依赖于 Android 框架,它通常用于执行后台的、非 UI 相关的周期性任务。
核心思想:
Timer 是一个调度器,TimerTask 是一个实现了 Runnable 接口的任务,你将 TimerTask 提交给 Timer,并指定执行时间。
关键参数 (以 schedule 方法为例):
-
TimerTask task(必需参数)
(图片来源网络,侵删)- 类型:
java.util.TimerTask - 描述: 你要执行的具体任务,需要继承
TimerTask并重写run()方法。
- 类型:
-
long delay(必需参数)- 类型:
long - 描述: 首次执行前的 延迟时间,单位是毫秒。
- 类型:
-
long period(必需参数)- 类型:
long - 描述: 周期,即两次执行之间的时间间隔,单位是毫秒,这个参数使得任务可以重复执行。
- 类型:
完整示例代码:
import java.util.Timer;
import java.util.TimerTask;
public class TimerExample {
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
if (timer != null) {
timer.cancel();
}
timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
// 注意:这个 run() 方法运行在后台线程,不能直接操作 UI!
// 可以在这里进行网络请求、文件读写等。
System.out.println("TimerTask executed on background thread: " + System.currentTimeMillis());
}
};
// 参数1: 任务
// 参数2: 首次执行的延迟时间 (毫秒)
// 参数3: 执行周期 (毫秒)
timer.schedule(timerTask, 1000, 2000); // 1秒后开始,每2秒执行一次
}
public void stopTimer() {
if (timer != null) {
timer.cancel(); // 取消整个定时器
timer.purge(); // 清理已取消的任务
timer = null;
}
}
}
优点:
- 简单直接,适合纯 Java 环境。
- 对于后台任务,逻辑清晰。
缺点:
- 不能直接更新 UI,因为它运行在后台线程。
- 如果任务执行时间超过了
period,可能会导致任务堆积。 - 不如
Handler方式与 Android 生命周期结合紧密。
ScheduledExecutorService (现代、强大的后台任务调度器)
这是 Java 并发包 (java.util.concurrent) 提供的更现代、更强大的调度工具,它是 Timer 的升级版,功能更强大,性能更好。
核心思想: 创建一个线程池,然后向这个线程池提交“延迟执行”或“周期性执行”的任务。
关键参数 (以 scheduleAtFixedRate 方法为例):
-
Runnable command(必需参数)- 类型:
java.lang.Runnable - 描述: 你要执行的任务。
- 类型:
-
long initialDelay(必需参数)- 类型:
long - 描述: 首次执行前的 初始延迟,单位是 纳秒 (但通常用毫秒)。
- 类型:
-
long period(必需参数)- 类型:
long - 描述: 周期,即两次执行开始时间之间的时间间隔,单位是 纳秒 (但通常用毫秒)。
- 类型:
完整示例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ExecutorTimerExample {
private ScheduledExecutorService scheduledExecutorService;
public void startTimer() {
// 创建一个单线程的调度器
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
// 定义任务
Runnable task = () -> {
// 运行在后台线程,不能直接操作 UI
System.out.println("ExecutorService task executed: " + System.currentTimeMillis());
};
// 参数1: 任务
// 参数2: 初始延迟 (秒)
// 参数3: 周期 (秒)
// 参数4: 时间单位
scheduledExecutorService.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);
}
public void stopTimer() {
if (scheduledExecutorService != null && !scheduledExecutorService.isShutdown()) {
scheduledExecutorService.shutdown(); // 优雅关闭,会执行完当前任务
// scheduledExecutorService.shutdownNow(); // 立即关闭,尝试中断正在执行的任务
}
}
}
优点:
- 功能强大,支持更复杂的调度策略。
- 基于线程池,性能和可靠性优于
Timer。 - 可以更好地控制线程的生命周期。
缺点:
- 同样,不能直接更新 UI。
- API 比
Handler稍复杂。
CountDownTimer (倒计时专用)
如果你需要的是一个倒计时功能(验证码倒计时、游戏倒计时),CountDownTimer 是最佳选择。
核心思想:
Android SDK 提供的专门用于倒计时的工具类,内部已经用 Handler 实现,使用起来非常方便。
关键参数:
-
long millisInFuture(必需参数)- 类型:
long - 描述: 倒计时的总时长,单位是 毫秒。
- 类型:
-
long countDownInterval(必需参数)- 类型:
long - 描述: 倒计时的 间隔,单位是 毫秒,系统会每隔这个时间调用一次
onTick()方法,传入1000表示每秒更新一次。
- 类型:
关键方法 (而非参数):
onTick(long millisUntilFinished): 每隔countDownInterval毫秒被调用一次,参数millisUntilFinished是剩余的毫秒数。onFinish(): 倒计时结束时被调用一次。
完整示例代码:
public class MainActivity extends AppCompatActivity {
private TextView countdownTextView;
private CountDownTimer countDownTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
countdownTextView = findViewById(R.id.countdown_text);
Button startButton = findViewById(R.id.start_countdown_button);
startButton.setOnClickListener(v -> startCountdown());
}
private void startCountdown() {
// 如果已有计时器在运行,先取消它
if (countDownTimer != null) {
countDownTimer.cancel();
}
// 参数1: 总时长 (10秒 = 10000毫秒)
// 参数2: 间隔 (1秒 = 1000毫秒)
countDownTimer = new CountDownTimer(10000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
// 更新 UI
countdownTextView.setText("剩余: " + (millisUntilFinished / 1000) + " 秒");
}
@Override
public void onFinish() {
// 倒计时结束
countdownTextView.setText("倒计时结束!");
}
}.start(); // 别忘了调用 start() 方法
}
@Override
protected void onDestroy() {
super.onDestroy();
if (countDownTimer != null) {
countDownTimer.cancel();
}
}
}
优点:
- API 设计非常符合倒计时场景,使用极其简单。
- 内部处理了线程问题,可以直接在
onTick和onFinish中更新 UI。 - 自动管理,不易出错。
缺点:
- 仅适用于倒计时场景,不能用于正计时。
协程 (Coroutines) - 现代 Android 开发首选
对于现代 Android 开发(Kotlin),协程是处理异步任务(包括定时器)的官方推荐方式,它代码更简洁,可读性更高,且能更好地处理生命周期。
核心思想:
通过 delay() 函数来实现延迟,而不是传统的回调,代码看起来像同步代码,但执行过程是异步的。
关键参数/函数:
delay(timeMillis: Long)(核心函数)- 类型: 挂起函数
- 描述: 暂协当前协程指定的毫秒数,它不会阻塞线程,只是让出线程的执行权。
完整示例代码 (使用 lifecycleScope):
// 在 Activity 或 Fragment 中
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
private val textView: TextView by lazy { findViewById(R.id.textView) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Button startButton).setOnClickListener {
startTimer()
}
}
private fun startTimer() {
// lifecycleScope 会自动在 Activity/Fragment 销毁时取消协程,防止内存泄漏
lifecycleScope.launch {
repeat(10) { i -> // 重复10次
delay(1000) // 延迟1秒
textView.text = "计时: ${i + 1}"
}
textView.text = "计时完成!"
}
}
}
优点:
- 代码简洁优雅,结构清晰,易于维护。
- 内置生命周期管理,使用
lifecycleScope或viewModelScope可以自动取消任务,避免内存泄漏。 - 不阻塞主线程,性能好。
- 可以轻松组合复杂的异步逻辑。
缺点:
- 需要学习 Kotlin 协程的概念,对于 Java 开发者有一定门槛。
总结与选择建议
| 方法 | 核心参数 | 适用场景 | 线程 | 优点 | 缺点 |
|---|---|---|---|---|---|
| Handler + postDelayed | Runnable, delayMillis |
UI 更新、正计时 | 主线程 | 灵活,与Android框架集成度高 | 需手动管理,易内存泄漏 |
| java.util.Timer | TimerTask, delay, period |
简单后台任务 | 后台线程 | Java标准,简单 | 不能更新UI,功能有限 |
| ScheduledExecutorService | Runnable, initialDelay, period |
复杂后台任务调度 | 后台线程 | 功能强大,性能好,线程池管理 | 不能更新UI,API稍复杂 |
| CountDownTimer | millisInFuture, countDownInterval |
倒计时 (如验证码) | 主线程 | API专为倒计时设计,简单易用 | 仅限倒计时 |
| 协程 (Coroutines) | delay(timeMillis) |
现代Android开发 (所有异步) | 主/后台均可 | 代码简洁,生命周期安全,是未来趋势 | 需要学习Kotlin协程 |
如何选择?
- 如果要在界面上做倒计时(如60秒重发验证码):首选
CountDownTimer。 - 如果要在界面上做正计时(如秒表、游戏计时):首选
Handler+postDelayed()。 - 如果要做纯后台的、非UI的周期性任务(如轮询服务器):首选
ScheduledExecutorService。 - 如果你正在用 Kotlin 开发新项目:强烈推荐使用 协程,它是处理所有异步(包括定时器)任务的最佳实践。
