ภาพรวม
การลบ Notification อัตโนมัติหลังจากเวลาที่กำหนด (Auto-dismiss) เหมาะสำหรับ Notification แบบชั่วคราว เช่น Toast-style, Promo, หรือ Reminder ที่ไม่ต้องการให้ค้างอยู่ใน Shade ใช้ Handler.postDelayed() หรือ AlarmManager / WorkManager ร่วมกับ NotificationManager.cancel()
ตัวอย่าง: ใช้ Handler.postDelayed() (Simple, ใน Process เดียว)
ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่
Android Studio → New Project → Empty Activity (Package: app.tutorialspoint.com.notifyme)
ขั้นตอนที่ 2: Layout (res/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"
android:padding="16dp"
tools:context=".MainActivity">
<Button
android:id="@+id/btnCreateNotification"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Create Auto-dismiss Notification (5 sec)"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
ขั้นตอนที่ 3: MainActivity.java
package app.tutorialspoint.com.notifyme;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import androidx.core.app.NotificationCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private static final String DEFAULT_CHANNEL_ID = "default";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnCreateNotification = findViewById(R.id.btnCreateNotification);
btnCreateNotification.setOnClickListener(v -> createAutoDismissNotification());
}
private void createAutoDismissNotification() {
// Intent สำหรับ Content Tap
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent resultIntent = PendingIntent.getActivity(
this, 0, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// สร้าง Notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Auto-dismiss Notification")
.setContentText("This will disappear in 5 seconds")
.setContentIntent(resultIntent)
.setStyle(new NotificationCompat.InboxStyle()
.addLine("Line 1: Auto-dismiss demo")
.addLine("Line 2: Using Handler.postDelayed")
.setBigContentTitle("Details")
.setSummaryText("Expires in 5s"))
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// สร้าง Channel (Android 8.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
"Auto-dismiss Notifications",
importance
);
channel.setDescription("Notifications that auto-dismiss after timeout");
manager.createNotificationChannel(channel);
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
}
// ID สำหรับ Notification นี้ (ต้องจำไว้เพื่อ Cancel)
final int notificationId = (int) System.currentTimeMillis();
// แสดง Notification
manager.notify(notificationId, builder.build());
// ตั้งเวลาลบอัตโนมัติ 5 วินาที (5000 ms)
long delayInMilliseconds = 5000;
new Handler(Looper.getMainLooper()).postDelayed(() -> {
manager.cancel(notificationId);
// Optional: Log หรือ Callback
// Log.d("AutoDismiss", "Notification " + notificationId + " cancelled");
}, delayInMilliseconds);
}
}
ขั้นตอนที่ 4: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.tutorialspoint.com.notifyme">
<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>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
วิธีการทำงาน
- สร้าง Notification พร้อม ID ที่ Unique (ใช้ timestamp)
- เรียก
manager.notify(id, builder.build())แสดง Notification - ใช้
Handler.postDelayed(() -> manager.cancel(id), delay)ตั้งเวลาลบ - หลังจาก Delay (5 วินาที) ระบบจะเรียก
cancel(id)→ Notification หายจาก Shade
ข้อจำกัดของ Handler.postDelayed()
| ข้อจำกัด | คำอธิบาย |
|---|---|
| Process Death | หากแอปถูก Kill (Swipe from Recent, System kill) Handler จะหยุดทำงาน → Notification ไม่ถูกลบ |
| Doze Mode | ใน Doze Mode Handler อาจล่าช้า |
| Boot/Reboot | ไม่รองรับการตั้งเวลาหลัง Reboot |
วิธีที่ทนทานกว่า: WorkManager (แนะนำสำหรับ Production)
WorkManager 保证工作即使在应用被杀死或设备重启后也会执行
1. เพิ่ม Dependency (build.gradle)
implementation "androidx.work:work-runtime-ktx:2.9.0"
2. สร้าง Worker
package app.tutorialspoint.com.notifyme;
import android.app.NotificationManager;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class CancelNotificationWorker extends Worker {
public static final String KEY_NOTIFICATION_ID = "notification_id";
public CancelNotificationWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
int notificationId = getInputData().getInt(KEY_NOTIFICATION_ID, -1);
if (notificationId != -1) {
NotificationManager manager = (NotificationManager)
getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
return Result.success();
}
return Result.failure();
}
}
3. ใช้งานใน MainActivity
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import java.util.concurrent.TimeUnit;
// แทนที่ Handler.postDelayed
OneTimeWorkRequest cancelWork = new OneTimeWorkRequest.Builder(CancelNotificationWorker.class)
.setInputData(new androidx.work.Data.Builder()
.putInt(CancelNotificationWorker.KEY_NOTIFICATION_ID, notificationId)
.build())
.setInitialDelay(5, TimeUnit.SECONDS)
.build();
WorkManager.getInstance(this).enqueue(cancelWork);
วิธีอื่น: AlarmManager (Exact Timing)
เหมาะสำหรับเวลาที่แม่นยำ (Exact) และรองรับ Boot/Doze
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent cancelIntent = new Intent(this, CancelNotificationReceiver.class);
cancelIntent.putExtra("notification_id", notificationId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, notificationId, cancelIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// ตั้งเวลา Exact (API 19+)
alarmManager.setExact(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + 5000, pendingIntent);
BroadcastReceiver:
public class CancelNotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int notificationId = intent.getIntExtra("notification_id", -1);
if (notificationId != -1) {
NotificationManager manager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
}
}
การเลือกวิธีที่เหมาะสม
| วิธี | เหมาะสำหรับ | ข้อดี | ข้อเสีย |
|---|---|---|---|
| Handler.postDelayed | สั้น (< 1 นาที), Foreground App | ง่าย, ไม่ต้อง Permission, เร็ว | ตายกับ Process, ไม่ Exact |
| WorkManager | ปานกลาง (นาที-ชั่วโมง), ต้องความน่าเชื่อถือ | Survive Process Death, Doze, Reboot, Battery Optimized | ไม่ Exact (มี Window), Overhead เล็กน้อย |
| AlarmManager (setExact) | Exact Time, Alarm, Reminder | แม่นยำ, Wake Device, Survive Reboot | ต้อง Permission SCHEDULE_EXACT_ALARM (API 31+), ใช้ Battery มาก |
| Notification Timeout (API 26+) | Auto-dismiss โดยระบบ | ไม่ต้องเขียนโค้ดจัดการ, ระบบจัดการให้ | ไม่สามารถควบคุมเวลาได้เอง (System Decides) |
Notification Timeout (API 26+) - วิธีง่ายที่สุด
// ตั้งค่า Timeout ให้ระบบจัดการลบเอง (หน่วย: milliseconds) builder.setTimeoutAfter(5000); // 5 วินาที // หรือใน Channel channel.setTimeoutAfter(5000); // ใช้ได้ API 26+
การทดสอบ
- รันแอป กดปุ่มสร้าง Notification
- นับ 5 วินาที → Notification จะหายจาก Shade เอง
- ลองกด Home → เปิดแอปอื่น → รอ 5 วินาที → กลับมา Shade จะหาย
- ทดสอง Kill App (Swipe Recent) → รอ 5 วินาที → Notification คงอยู่ (Handler ตาย) → ทดสอบ WorkManager จะหาย
แนะนำ: ใช้ builder.setTimeoutAfter(5000) สำหรับกรณีง่าย (API 26+) ใช้ WorkManager สำหรับ Production ที่ต้องการความน่าเชื่อถือ ใช้ AlarmManager สำหรับ Exact Time Alarm/Reminder