Android實(shí)現(xiàn)界面定時(shí)刷新功能
一、項(xiàng)目介紹
在移動(dòng)應(yīng)用中,界面定時(shí)刷新是非常常見(jiàn)的需求,典型場(chǎng)景包括:
時(shí)鐘/秒表:每秒更新顯示當(dāng)前時(shí)間或計(jì)時(shí);
實(shí)時(shí)數(shù)據(jù)監(jiān)控:定期拉取服務(wù)器狀態(tài)或傳感器數(shù)據(jù)并更新 UI;
列表自動(dòng)刷新:如新聞、社交Feeds 定時(shí)刷新最新內(nèi)容;
倒計(jì)時(shí):在促銷(xiāo)、考試倒計(jì)時(shí)場(chǎng)景下每秒更新剩余時(shí)長(zhǎng);
游戲邏輯刷新:簡(jiǎn)單動(dòng)畫(huà)或狀態(tài)輪詢(xún)。
本教程將以一個(gè)“實(shí)時(shí)時(shí)鐘”示例為主線(xiàn),演示多種常用的定時(shí)刷新的實(shí)現(xiàn)方式:
方案 A:Handler + postDelayed
方案 B:Timer + Handler
方案 C:ScheduledExecutorService
方案 D:RxJava interval
方案 E:Kotlin Coroutines + Flow
并對(duì)比它們的代碼簡(jiǎn)潔度、性能消耗、生命周期管理與取消機(jī)制,幫助您在項(xiàng)目中快速選型并上手。
二、相關(guān)技術(shù)與知識(shí)
主線(xiàn)程與 UI 更新
Android 要求所有 UI 操作必須在主線(xiàn)程(UI 線(xiàn)程)中執(zhí)行。
定時(shí)任務(wù)若在子線(xiàn)程中執(zhí)行,需要切回主線(xiàn)程使用
runOnUiThread()或Handler。
Handler & Looper
Handler:將Runnable或Message發(fā)布到所綁定的Looper(線(xiàn)程消息隊(duì)列)。postDelayed(Runnable r, long delayMillis):延遲執(zhí)行任務(wù)。
Timer & TimerTask
Timer用于安排TimerTask在后臺(tái)線(xiàn)程周期或延遲執(zhí)行;結(jié)果需要通過(guò)
Handler或runOnUiThread()回到主線(xiàn)程。
ScheduledExecutorService
Java 標(biāo)準(zhǔn)庫(kù):
Executors.newSingleThreadScheduledExecutor()可定期執(zhí)行任務(wù),支持scheduleAtFixedRate()。
RxJava
Observable.interval():基于 Scheduler 定時(shí)發(fā)射 Long 值,結(jié)合observeOn(AndroidSchedulers.mainThread())更新 UI。
Kotlin Coroutines & Flow
flow { while(true) { emit(Unit); delay(1000) } }:使用協(xié)程輕量定時(shí);在
lifecycleScope.launchWhenStarted中收集并更新 UI。
生命周期管理與取消
定時(shí)任務(wù)應(yīng)在
Activity.onPause()/onDestroy()中取消,避免內(nèi)存泄漏與后臺(tái)無(wú)用計(jì)算;不同方案的取消方式也不同:
Handler.removeCallbacks(),timer.cancel(),future.cancel(),disposable.dispose(),job.cancel()等。
三、實(shí)現(xiàn)思路
示例界面設(shè)計(jì)
一個(gè)
TextView用于顯示當(dāng)前時(shí)間(格式:HH:mm:ss);一個(gè)“開(kāi)始”與“停止”按鈕,控制定時(shí)刷新;
布局簡(jiǎn)單,整合到 MainActivity 注釋中。
核心方法封裝
updateTime():獲取系統(tǒng)當(dāng)前時(shí)間并格式化tvClock.setText(...);對(duì)于每種方案,在“開(kāi)始”時(shí)啟動(dòng)定時(shí)任務(wù),每次調(diào)用
updateTime();在“停止”時(shí)取消任務(wù)并停止刷新。
生命周期鉤子
在
onPause()或onDestroy()中統(tǒng)一調(diào)用stopX()方法,確保任務(wù)取消。
對(duì)比與選型
在項(xiàng)目初期可使用最簡(jiǎn)單的
Handler.postDelayed;若需要高可控或并發(fā)任務(wù),可選擇
ScheduledExecutorService;如果已引入 RxJava 或使用 Kotlin,推薦相應(yīng)方案。
四、完整代碼
// ==============================================
// 文件:MainActivity.java
// 功能:演示五種方式實(shí)現(xiàn)界面定時(shí)刷新(實(shí)時(shí)時(shí)鐘示例)
// 包含布局 XML、Gradle 依賴(lài)一處整合,詳細(xì)注釋
// ==============================================
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 依賴(lài)
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 依賴(lài)(需 Kotlin 支持)
// import kotlinx.coroutines.*
// import kotlinx.coroutines.flow.*
// Java 并發(fā)
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();
});
}
/** 更新時(shí)鐘顯示 */
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="開(kāi)始刷新"/>
<Button
android:id="@+id/btnStop"
android:layout_marginStart="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止刷新"/>
</LinearLayout>
</LinearLayout>
=========================== 布局結(jié)束 ===========================
*/
/*
=========================== app/build.gradle 關(guān)鍵依賴(lài) ===========================
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 結(jié)束 ===========================
*/五、方法解讀
Handler + postDelayed
優(yōu)點(diǎn):代碼最簡(jiǎn)潔,無(wú)額外依賴(lài);
缺點(diǎn):?jiǎn)尉€(xiàn)程串行,不易并行多個(gè)定時(shí)任務(wù);
Timer + Handler
優(yōu)點(diǎn):邏輯清晰,直接在后臺(tái)線(xiàn)程周期執(zhí)行;
缺點(diǎn):
Timer無(wú)法感知 UI 生命周期,需要手動(dòng)取消;若任務(wù)拋異常會(huì)終止調(diào)度。
ScheduledExecutorService
優(yōu)點(diǎn):功能強(qiáng)大,可自定義線(xiàn)程池大小與策略;
缺點(diǎn):稍顯冗長(zhǎng),但更適合多個(gè)并發(fā)定時(shí)任務(wù)。
RxJava interval
優(yōu)點(diǎn):鏈?zhǔn)秸{(diào)用、易于組合,可與其他 Rx 流無(wú)縫銜接;
缺點(diǎn):需引入 RxJava 庫(kù),學(xué)習(xí)門(mén)檻較高;
Kotlin Coroutines + Flow
優(yōu)點(diǎn):語(yǔ)言級(jí)支持,寫(xiě)法像同步,易讀易寫(xiě);
缺點(diǎn):需 Kotlin 環(huán)境,注意協(xié)程生命周期管理;
六、項(xiàng)目總結(jié)
選擇建議:
最簡(jiǎn)單:僅需單一定時(shí)在 UI 線(xiàn)程更新 → 方案 A;
復(fù)用需求:多處定時(shí)、可復(fù)用同一線(xiàn)程 → 方案 B/C;
已有 RxJava:推薦 方案 D;
Kotlin 項(xiàng)目:首選 方案 E;
生命周期注意:所有定時(shí)任務(wù)都需在
onDestroy()、onPause()或onStop()中取消,避免內(nèi)存泄漏與后臺(tái)無(wú)用計(jì)算。精細(xì)調(diào)度:
若對(duì)“漂移”敏感,可使用
scheduleAtFixedRate();若后續(xù)任務(wù)執(zhí)行時(shí)間不可控,推薦
scheduleWithFixedDelay()。
性能優(yōu)化:避免在定時(shí)任務(wù)中執(zhí)行過(guò)重運(yùn)算或 Block UI;對(duì)于高精度(<100ms)定時(shí)可選更底層方案如
Choreographer。
到此這篇關(guān)于Android實(shí)現(xiàn)界面定時(shí)刷新功能的文章就介紹到這了,更多相關(guān)Android界面定時(shí)刷新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)登錄界面的注冊(cè)功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)登錄界面的注冊(cè)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android Fragment中使用SurfaceView切換時(shí)閃一下黑屏的解決辦法
本篇文章主要給大家分享Android Fragment中使用SurfaceView切換時(shí)閃一下黑屏的解決辦法,需要的朋友可以參考下2015-09-09
Android仿微信Viewpager-Fragment惰性加載(lazy-loading)
這篇文章主要為大家詳細(xì)介紹了Android仿微信Viewpager-Fragment惰性加載lazy-loading,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Kotlin 作用域函數(shù) let 的實(shí)現(xiàn)原理示例解析
Kotlin 中的let是一個(gè),它廣泛用于作用域函數(shù)(Scope Functions)中,尤其適用于對(duì)可空對(duì)象(nullable)做非空判斷并執(zhí)行代碼塊的場(chǎng)景,本文給大家介紹Kotlin 作用域函數(shù) let 的實(shí)現(xiàn)原理,感興趣的朋友一起看看吧2025-07-07
Android開(kāi)發(fā)簡(jiǎn)易音樂(lè)播放器
這篇文章主要為大家詳細(xì)介紹了Android開(kāi)發(fā)簡(jiǎn)易音樂(lè)播放器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
使用AndroidStudio上傳忽略文件至SVN Server的解決辦法
這篇文章主要介紹了使用AndroidStudio上傳忽略文件至SVN Server的解決辦法 的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
Android ImageView 固定寬高比例的實(shí)現(xiàn)方法
這篇文章主要介紹了Android ImageView 固定寬高比例的實(shí)現(xiàn)方法的相關(guān)資料,,方法一:設(shè)置 adjustViewBounds="true",方法二:使用 Universal-Image-Loader 圖片緩存類(lèi),需要注意的是方法二和方法一同時(shí)使用導(dǎo)致設(shè)置無(wú)效,需要的朋友可以參考下2017-07-07

