Giới thiệu
Bài viết này hướng dẫn cách thực thi một phương thức (method) lặp lại mỗi 10 giây trên Android bằng cách sử dụng Handler và Runnable. Đây là cách phổ biến cho các tác vụ định kỳ nhẹ trên UI thread.
Các bước thực hiện
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.
Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="24sp" />
</LinearLayout>
Bước 3: Viết code lặp định kỳ (src/MainActivity.java)
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private Handler handler;
private Runnable runnable;
private final int delay = 10000; // 10 giây = 10,000 ms
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Khởi tạo Handler gắn với Main Looper (UI thread)
handler = new Handler(Looper.getMainLooper());
}
@Override
protected void onResume() {
super.onResume();
startPeriodicTask();
}
@Override
protected void onPause() {
super.onPause();
stopPeriodicTask();
}
private void startPeriodicTask() {
runnable = new Runnable() {
@Override
public void run() {
// Thực hiện công việc ở đây
doPeriodicWork();
// Lên lịch chạy lại sau 10 giây
handler.postDelayed(this, delay);
}
};
// Khởi chạy lần đầu
handler.post(runnable);
}
private void stopPeriodicTask() {
if (handler != null && runnable != null) {
handler.removeCallbacks(runnable);
runnable = null;
}
}
private void doPeriodicWork() {
// Ví dụ: Hiển thị Toast mỗi 10 giây
Toast.makeText(this, "Phương thức chạy mỗi 10 giây", Toast.LENGTH_SHORT).show();
// Thực tế: gọi API, cập nhật UI, sync dữ liệu, check trạng thái...
}
}
Bước 4: Cấu hình Manifest (androidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
</intent-filter>
</activity>
</application>
</manifest>
Chạy ứng dụng
Kết nối thiết bị, nhấn Run. Mỗi 10 giây sẽ hiển thị một Toast. Test bằng cách đưa app vào background (nhấn Home) - Toast sẽ dừng, quay lại app - Toast tiếp tục.
So sánh các phương pháp chạy định kỳ
| Phương pháp | Ưu điểm | Nhược điểm | Phù hợp cho |
|---|---|---|---|
| Handler + Runnable | Đơn giản, chạy trên UI thread, dễ cập nhật UI | Dừng khi app background, không chính xác nếu tác vụ nặng | Cập nhật UI nhẹ, polling ngắn hạn |
| ScheduledExecutorService | Chạy background thread, chính xác hơn | Cần chuyển về UI thread để cập nhật view | Tác vụ nặng, không liên quan UI |
| WorkManager | Đảm bảo chạy được ngay cả khi app kill, hệ thống tối ưu pin | Không chính xác về thời điểm (deferred), phức tạp hơn | Sync dữ liệu, backup, tác vụ quan trọng |
| AlarmManager + BroadcastReceiver | Đánh thức app từ background, chính xác (setExact) | Phức tạp, cần permission từ API 31+, tiêu tốn pin | Nhắc nhở, alarm, tác vụ thời gian thực |
| Kotlin Coroutines + Flow | Hiện đại, structured concurrency, dễ hủy, test | Yêu cầu Kotlin, hiểu về coroutine scope | Dự án Kotlin mới, logic phức tạp |
Ví dụ với Kotlin Coroutines (Khuyên dùng cho dự án mới)
// Trong ViewModel hoặc Lifecycle-aware component
viewModelScope.launch {
while (true) {
doPeriodicWork() // suspend function hoặc withContext(Dispatchers.IO)
delay(10_000) // 10 giây, tự động hủy khi viewModelScope bị clear
}
}
Lưu ý quan trọng
- Luôn dừng trong onPause()/onStop(): Tránh leak và crash khi Activity bị destroy.
- Handler(Looper.getMainLooper()): Khai báo rõ ràng từ API 30+, tránh warning.
- Không làm tác vụ nặng trong Runnable: Sẽ gây lag UI. Dùng
withContext(Dispatchers.IO)hoặc ExecutorService. - WorkManager là lựa chọn tốt nhất cho tác vụ cần đảm bảo thực thi (sync, upload log...).
- Tránh
TimerTask- đã lỗi thời và không quản lý vòng đời Android.