Android實(shí)現(xiàn)界面定時(shí)刷新功能
一、項(xiàng)目介紹
在移動(dòng)應(yīng)用中,界面定時(shí)刷新是非常常見的需求,典型場(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í):在促銷、考試倒計(jì)時(shí)場(chǎng)景下每秒更新剩余時(shí)長(zhǎng);
游戲邏輯刷新:簡(jiǎn)單動(dòng)畫或狀態(tài)輪詢。
本教程將以一個(gè)“實(shí)時(shí)時(shí)鐘”示例為主線,演示多種常用的定時(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í)
主線程與 UI 更新
Android 要求所有 UI 操作必須在主線程(UI 線程)中執(zhí)行。
定時(shí)任務(wù)若在子線程中執(zhí)行,需要切回主線程使用
runOnUiThread()或Handler。
Handler & Looper
Handler:將Runnable或Message發(fā)布到所綁定的Looper(線程消息隊(duì)列)。postDelayed(Runnable r, long delayMillis):延遲執(zhí)行任務(wù)。
Timer & TimerTask
Timer用于安排TimerTask在后臺(tái)線程周期或延遲執(zhí)行;結(jié)果需要通過
Handler或runOnUiThread()回到主線程。
ScheduledExecutorService
Java 標(biāo)準(zhǔn)庫:
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)無用計(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è)“開始”與“停止”按鈕,控制定時(shí)刷新;
布局簡(jiǎn)單,整合到 MainActivity 注釋中。
核心方法封裝
updateTime():獲取系統(tǒng)當(dāng)前時(shí)間并格式化tvClock.setText(...);對(duì)于每種方案,在“開始”時(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 依賴一處整合,詳細(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 依賴
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 并發(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="開始刷新"/>
<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)鍵依賴 ===========================
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)潔,無額外依賴;
缺點(diǎn):?jiǎn)尉€程串行,不易并行多個(gè)定時(shí)任務(wù);
Timer + Handler
優(yōu)點(diǎn):邏輯清晰,直接在后臺(tái)線程周期執(zhí)行;
缺點(diǎn):
Timer無法感知 UI 生命周期,需要手動(dòng)取消;若任務(wù)拋異常會(huì)終止調(diào)度。
ScheduledExecutorService
優(yōu)點(diǎn):功能強(qiáng)大,可自定義線程池大小與策略;
缺點(diǎn):稍顯冗長(zhǎng),但更適合多個(gè)并發(fā)定時(shí)任務(wù)。
RxJava interval
優(yōu)點(diǎn):鏈?zhǔn)秸{(diào)用、易于組合,可與其他 Rx 流無縫銜接;
缺點(diǎn):需引入 RxJava 庫,學(xué)習(xí)門檻較高;
Kotlin Coroutines + Flow
優(yōu)點(diǎn):語言級(jí)支持,寫法像同步,易讀易寫;
缺點(diǎn):需 Kotlin 環(huán)境,注意協(xié)程生命周期管理;
六、項(xiàng)目總結(jié)
選擇建議:
最簡(jiǎn)單:僅需單一定時(shí)在 UI 線程更新 → 方案 A;
復(fù)用需求:多處定時(shí)、可復(fù)用同一線程 → 方案 B/C;
已有 RxJava:推薦 方案 D;
Kotlin 項(xiàng)目:首選 方案 E;
生命周期注意:所有定時(shí)任務(wù)都需在
onDestroy()、onPause()或onStop()中取消,避免內(nèi)存泄漏與后臺(tái)無用計(jì)算。精細(xì)調(diào)度:
若對(duì)“漂移”敏感,可使用
scheduleAtFixedRate();若后續(xù)任務(wù)執(zhí)行時(shí)間不可控,推薦
scheduleWithFixedDelay()。
性能優(yōu)化:避免在定時(shí)任務(wù)中執(zhí)行過重運(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)網(wǎng)絡(luò)多線程斷點(diǎn)續(xù)傳下載實(shí)例
本示例介紹在Android平臺(tái)下通過HTTP協(xié)議實(shí)現(xiàn)斷點(diǎn)續(xù)傳下載。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-10-10
基于Flutter制作一個(gè)長(zhǎng)按展示操作項(xiàng)面板的桌面圖標(biāo)
Flutter是一種強(qiáng)大的跨平臺(tái)移動(dòng)應(yīng)用程序框架,它能夠幫助開發(fā)者輕松地創(chuàng)建漂亮、快速、高效的應(yīng)用程序,本文的主題是如何在Flutter中制作一個(gè)長(zhǎng)按展示操作項(xiàng)面板的桌面圖標(biāo),在某些場(chǎng)景下,這個(gè)功能會(huì)讓應(yīng)用程序更加便利和易用2023-06-06
Flutter基于Dart Unwrapping Multiple Optional小技巧
這篇文章主要為大家介紹了Flutter Unwrapping Multiple Optional打開多個(gè)選項(xiàng)小技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Android Caused by: java.lang.ClassNotFoundException解決辦法
這篇文章主要介紹了Android Caused by: java.lang.ClassNotFoundException解決辦法的相關(guān)資料,需要的朋友可以參考下2017-03-03
Android自定義RadioGroupX實(shí)現(xiàn)多行多列布局
這篇文章主要為大家詳細(xì)介紹了Android自定義RadioGroupX實(shí)現(xiàn)多行多列布局,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Flutter?隊(duì)列任務(wù)的實(shí)現(xiàn)
本文主要介紹了Flutter?隊(duì)列任務(wù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
如何通過Android Stduio來編寫一個(gè)完整的天氣預(yù)報(bào)APP
這篇文章主要介紹了通過Android Stduio來編寫一個(gè)天氣預(yù)報(bào)APP,具體的實(shí)現(xiàn)是通過調(diào)用天氣預(yù)報(bào)接口來獲得天氣數(shù)據(jù),再將查詢的天氣信息存儲(chǔ)在SQLiteDatabase中,界面則用LIstView和GridView來搭建2021-08-08
Android RadarView雷達(dá)圖(蜘蛛網(wǎng)圖)的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android RadarView雷達(dá)圖(蜘蛛網(wǎng)圖)的實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03

