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