รัน IntentService แบบ Periodic ทุก 10 วินาที
บทความนี้สาธิตวิธีการเริ่ม IntentService ทุก 10 วินาทีโดยใช้ Timer + TimerTask ร่วมกับ Handler เพื่อสลับมาทำงานบน Main Thread ก่อนเริ่ม Service
⚠️ ข้อควรทราบก่อนใช้งาน
IntentService ถูก Deprecated ตั้งแต่ API 30 และการใช้ Timer เพื่อรัน Service บ่อยๆ อาจส่งผลต่อแบตเตอรี่และประสิทธิภาพ ระบบ Android อาจจำกัดการทำงานใน Background (Doze mode, App Standby)
แนะนำให้ใช้ WorkManager กับ PeriodicWorkRequest แทน ซึ่งจัดการทุกอย่างให้อัตโนมัติ
ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่
Android Studio → File → New Project → Empty Activity
ขั้นตอนที่ 2: Layout - activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="รอการเริ่ม Service..."
android:textSize="18sp"
android:padding="24dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="หยุด Timer"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/tvStatus"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
ขั้นตอนที่ 3: IntentService Class
package com.example.periodicservicedemo;
import android.app.IntentService;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class PeriodicIntentService extends IntentService {
public static final String ACTION_UPDATE = "com.example.periodicservicedemo.UPDATE";
public static final String EXTRA_MESSAGE = "extra_message";
public static volatile boolean shouldStop = false;
public PeriodicIntentService() {
super("PeriodicIntentService");
}
@Override
public @Nullable IBinder onBind(Intent intent) {
return null;
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (shouldStop) {
stopSelf();
return;
}
// จำลองงาน Background (เช่น Sync ข้อมูล, ตรวจสอบสถานะ)
String result = "🔄 อัปเดตล่าสุด: " + java.text.DateFormat.getTimeInstance().format(new java.util.Date());
// ส่ง Broadcast กลับไป UI
Intent broadcastIntent = new Intent(ACTION_UPDATE);
broadcastIntent.putExtra(EXTRA_MESSAGE, result);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
}
}
ขั้นตอนที่ 4: MainActivity.java - จัดการ Timer
package com.example.periodicservicedemo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends AppCompatActivity {
private TextView tvStatus;
private Button btnStop;
private Timer timer;
private TimerTask timerTask;
private Handler mainHandler;
private BroadcastReceiver broadcastReceiver;
private boolean isRunning = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvStatus = findViewById(R.id.tvStatus);
btnStop = findViewById(R.id.btnStop);
mainHandler = new Handler(Looper.getMainLooper());
// BroadcastReceiver รับข้อมูลจาก Service
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (PeriodicIntentService.ACTION_UPDATE.equals(intent.getAction())) {
String message = intent.getStringExtra(PeriodicIntentService.EXTRA_MESSAGE);
tvStatus.setText(message);
}
}
};
btnStop.setOnClickListener(v -> stopPeriodicService());
// เริ่ม Timer ทันทีที่ Activity พร้อม
startPeriodicService();
}
private void startPeriodicService() {
if (isRunning) return;
isRunning = true;
PeriodicIntentService.shouldStop = false;
btnStop.setVisibility(View.VISIBLE);
tvStatus.setText("⏱ เริ่ม Timer - จะรันทุก 10 วินาที");
timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
// ต้องสลับมา Main Thread ก่อน startService
mainHandler.post(() -> {
if (!PeriodicIntentService.shouldStop) {
startService(new Intent(MainActivity.this, PeriodicIntentService.class));
}
});
}
};
// schedule(task, delay, period) - delay=0 เริ่มทันที, period=10000ms (10 วินาที)
timer.schedule(timerTask, 0, 10000);
}
private void stopPeriodicService() {
if (!isRunning) return;
isRunning = false;
PeriodicIntentService.shouldStop = true;
if (timerTask != null) timerTask.cancel();
if (timer != null) {
timer.cancel();
timer.purge();
}
btnStop.setVisibility(View.GONE);
tvStatus.setText("⏹ หยุด Timer แล้ว");
}
@Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver(
broadcastReceiver,
new IntentFilter(PeriodicIntentService.ACTION_UPDATE)
);
}
@Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
// ไม่หยุด Timer ที่นี่ เพื่อให้ทำงานต่อใน Background
// แต่ควรหยุดใน onDestroy() หากไม่ต้องการให้ทำงานตลอด
}
@Override
protected void onDestroy() {
super.onDestroy();
stopPeriodicService(); // หยุดสุดท้ายเมื่อ Activity ถูกทำลาย
}
}
ขั้นตอนที่ 5: AndroidManifest.xml
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.periodicservicedemo">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application ...>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".PeriodicIntentService" />
</application>
</manifest>
วิธีที่ดีกว่า: ใช้ WorkManager (แนะนำอย่างยิ่ง)
// build.gradle
implementation "androidx.work:work-runtime-ktx:2.9.0"
// สร้าง Worker
public class PeriodicSyncWorker extends CoroutineWorker {
public PeriodicSyncWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
// ทำงาน Sync ข้อมูลที่นี่
return Result.success();
}
}
// เรียกใช้ใน Activity/Application
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(
PeriodicSyncWorker.class,
15, TimeUnit.MINUTES) // ขั้นต่ำ 15 นาที (นโยบาย Android)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build();
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"periodic_sync",
ExistingPeriodicWorkPolicy.KEEP,
workRequest
);
เปรียบเทียบ Timer vs WorkManager
| คุณสมบัติ | Timer + IntentService | WorkManager |
|---|---|---|
| Interval ขั้นต่ำ | ได้ทุกวินาที | 15 นาที (Periodic) |
| Doze Mode / App Standby | ไม่รองรับ (อาจถูกหยุด) | รองรับอัตโนมัติ |
| Survive Reboot | ไม่ได้ | ได้ (WorkManager จัดการ) |
| Retry Policy | ต้องเขียนเอง | มี Built-in |
| Battery Optimization | ไม่มี | มีการจัดการอัจฉริยะ |
สรุป
แม้จะสามารถใช้ Timer + IntentService เพื่อรันทุก 10 วินาทีได้ แต่ไม่แนะนำสำหรับ Production App เนื่องจากส่งผลต่อแบตเตอรี่และอาจถูกระบบหยุด ควรใช้ WorkManager ซึ่งเป็นทางเลือกมาตรฐานของ Android Jetpack