一、项目介绍
在移动应用中,界面定时刷新是非常常见的需求,典型场景包括:
时钟/秒表:每秒更新显示当前时间或计时;
实时数据监控:定期拉取服务器状态或传感器数据并更新 ui;
列表自动刷新:如新闻、社交feeds 定时刷新最新内容;
倒计时:在促销、考试倒计时场景下每秒更新剩余时长;
游戏逻辑刷新:简单动画或状态轮询。
本教程将以一个“实时时钟”示例为主线,演示多种常用的定时刷新的实现方式:
方案 a:handler + postdelayed
方案 b:timer + handler
方案 c:scheduledexecutorservice
方案 d:rxjava interval
方案 e:kotlin coroutines + flow
并对比它们的代码简洁度、性能消耗、生命周期管理与取消机制,帮助您在项目中快速选型并上手。
二、相关技术与知识
主线程与 ui 更新
android 要求所有 ui 操作必须在主线程(ui 线程)中执行。
定时任务若在子线程中执行,需要切回主线程使用
runonuithread()或handler。
handler & looper
handler:将runnable或message发布到所绑定的looper(线程消息队列)。postdelayed(runnable r, long delaymillis):延迟执行任务。
timer & timertask
timer用于安排timertask在后台线程周期或延迟执行;结果需要通过
handler或runonuithread()回到主线程。
scheduledexecutorservice
java 标准库:
executors.newsinglethreadscheduledexecutor()可定期执行任务,支持scheduleatfixedrate()。
rxjava
observable.interval():基于 scheduler 定时发射 long 值,结合observeon(androidschedulers.mainthread())更新 ui。
kotlin coroutines & flow
flow { while(true) { emit(unit); delay(1000) } }:使用协程轻量定时;在
lifecyclescope.launchwhenstarted中收集并更新 ui。
生命周期管理与取消
定时任务应在
activity.onpause()/ondestroy()中取消,避免内存泄漏与后台无用计算;不同方案的取消方式也不同:
handler.removecallbacks(),timer.cancel(),future.cancel(),disposable.dispose(),job.cancel()等。
三、实现思路
示例界面设计
一个
textview用于显示当前时间(格式:hh:mm:ss);一个“开始”与“停止”按钮,控制定时刷新;
布局简单,整合到 mainactivity 注释中。
核心方法封装
updatetime():获取系统当前时间并格式化tvclock.settext(...);对于每种方案,在“开始”时启动定时任务,每次调用
updatetime();在“停止”时取消任务并停止刷新。
生命周期钩子
在
onpause()或ondestroy()中统一调用stopx()方法,确保任务取消。
对比与选型
在项目初期可使用最简单的
handler.postdelayed;若需要高可控或并发任务,可选择
scheduledexecutorservice;如果已引入 rxjava 或使用 kotlin,推荐相应方案。
四、完整代码
// ==============================================
// 文件:mainactivity.java
// 功能:演示五种方式实现界面定时刷新(实时时钟示例)
// 包含布局 xml、gradle 依赖一处整合,详细注释
// ==============================================
package com.example.timerrefresh;
import android.os.*;
import android.view.view;
import android.widget.button;
import android.widget.textview;
import androidx.appcompat.app.appcompatactivity;
// rxjava 依赖
import io.reactivex.rxjava3.android.schedulers.androidschedulers;
import io.reactivex.rxjava3.core.*;
import io.reactivex.rxjava3.disposables.disposable;
import io.reactivex.rxjava3.schedulers.schedulers;
// kotlin coroutines & flow 依赖(需 kotlin 支持)
// import kotlinx.coroutines.*
// import kotlinx.coroutines.flow.*
// java 并发
import java.text.simpledateformat;
import java.util.date;
import java.util.locale;
import java.util.timer;
import java.util.timertask;
import java.util.concurrent.*;
public class mainactivity extends appcompatactivity {
private textview tvclock;
private button btnstart, btnstop;
// --- 方案 a: handler + postdelayed ---
private handler handler = new handler(looper.getmainlooper());
private runnable taska = new runnable() {
@override public void run() {
updatetime();
handler.postdelayed(this, 1000);
}
};
// --- 方案 b: timer + handler ---
private timer timerb;
private timertask timertaskb;
// --- 方案 c: scheduledexecutorservice ---
private scheduledexecutorservice schedulerc;
private scheduledfuture<?> futurec;
// --- 方案 d: rxjava interval ---
private disposable disposabled;
// --- 方案 e: kotlin coroutines + flow ---
// private job jobe;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main); // 布局整合在注释下方
tvclock = findviewbyid(r.id.tvclock);
btnstart = findviewbyid(r.id.btnstart);
btnstop = findviewbyid(r.id.btnstop);
btnstart.setonclicklistener(v -> {
// 选择下面一种,注释其余
starta();
// startb();
// startc();
// startd();
// starte();
});
btnstop.setonclicklistener(v -> {
stopa();
stopb();
stopc();
stopd();
stope();
});
}
/** 更新时钟显示 */
private void updatetime() {
string now = new simpledateformat(
"hh:mm:ss", locale.getdefault())
.format(new date());
tvclock.settext(now);
}
// === 方案 a:handler + postdelayed ===
private void starta() {
handler.post(taska);
}
private void stopa() {
handler.removecallbacks(taska);
}
// === 方案 b:timer + handler ===
private void startb() {
timerb = new timer();
timertaskb = new timertask() {
@override public void run() {
handler.post(() -> updatetime());
}
};
timerb.scheduleatfixedrate(timertaskb, 0, 1000);
}
private void stopb() {
if (timerb != null) {
timerb.cancel();
timerb = null;
}
}
// === 方案 c:scheduledexecutorservice ===
private void startc() {
schedulerc = executors.newsinglethreadscheduledexecutor();
futurec = schedulerc.scheduleatfixedrate(() -> {
runonuithread(this::updatetime);
}, 0, 1, timeunit.seconds);
}
private void stopc() {
if (futurec != null) futurec.cancel(true);
if (schedulerc != null) schedulerc.shutdown();
}
// === 方案 d:rxjava interval ===
private void startd() {
disposabled = observable.interval(0, 1, timeunit.seconds)
.subscribeon(schedulers.io())
.observeon(androidschedulers.mainthread())
.subscribe(x -> updatetime());
}
private void stopd() {
if (disposabled != null && !disposabled.isdisposed()) {
disposabled.dispose();
}
}
// === 方案 e:kotlin coroutines + flow ===
/*
private void starte() {
jobe = coroutinescope(dispatchers.main).launch {
flow {
while (true) {
emit(unit)
delay(1000)
}
}.collect {
updatetime()
}
};
}
private void stope() {
if (jobe != null && jobe.isactive()) {
jobe.cancel();
}
}
*/
@override protected void ondestroy() {
super.ondestroy();
// 确保停止所有方案
stopa(); stopb(); stopc(); stopd(); stope();
}
}
/*
=========================== res/layout/activity_main.xml ===========================
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:gravity="center"
android:padding="24dp"
android:layout_width="match_parent" android:layout_height="match_parent">
<textview
android:id="@+id/tvclock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="--:--:--"
android:textsize="48sp"
android:textstyle="bold"/>
<linearlayout
android:orientation="horizontal"
android:layout_margintop="32dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<button
android:id="@+id/btnstart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始刷新"/>
<button
android:id="@+id/btnstop"
android:layout_marginstart="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止刷新"/>
</linearlayout>
</linearlayout>
=========================== 布局结束 ===========================
*/
/*
=========================== app/build.gradle 关键依赖 ===========================
dependencies {
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
// kotlin coroutines & flow(如需要示例 e)
// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
}
=========================== gradle 结束 ===========================
*/五、方法解读
handler + postdelayed
优点:代码最简洁,无额外依赖;
缺点:单线程串行,不易并行多个定时任务;
timer + handler
优点:逻辑清晰,直接在后台线程周期执行;
缺点:
timer无法感知 ui 生命周期,需要手动取消;若任务抛异常会终止调度。
scheduledexecutorservice
优点:功能强大,可自定义线程池大小与策略;
缺点:稍显冗长,但更适合多个并发定时任务。
rxjava interval
优点:链式调用、易于组合,可与其他 rx 流无缝衔接;
缺点:需引入 rxjava 库,学习门槛较高;
kotlin coroutines + flow
优点:语言级支持,写法像同步,易读易写;
缺点:需 kotlin 环境,注意协程生命周期管理;
六、项目总结
选择建议:
最简单:仅需单一定时在 ui 线程更新 → 方案 a;
复用需求:多处定时、可复用同一线程 → 方案 b/c;
已有 rxjava:推荐 方案 d;
kotlin 项目:首选 方案 e;
生命周期注意:所有定时任务都需在
ondestroy()、onpause()或onstop()中取消,避免内存泄漏与后台无用计算。精细调度:
若对“漂移”敏感,可使用
scheduleatfixedrate();若后续任务执行时间不可控,推荐
schedulewithfixeddelay()。
性能优化:避免在定时任务中执行过重运算或 block ui;对于高精度(<100ms)定时可选更底层方案如
choreographer。
到此这篇关于android实现界面定时刷新功能的文章就介绍到这了,更多相关android界面定时刷新内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论