Giới thiệu
Ví dụ này minh họa cách kiểm tra xem một thông báo cụ thể có đang hiển thị (visible) hay đã bị hủy (canceled) bằng cách sử dụng PendingIntent.FLAG_NO_CREATE.
Nguyên lý kỹ thuật
Khi tạo thông báo, ta gán một PendingIntent với requestCode cố định. Sau đó có thể kiểm tra xem PendingIntent đó còn tồn tại hay không bằng flag FLAG_NO_CREATE - nếu trả về null thì PendingIntent đã bị hủy → thông báo đã bị dismiss.
Các bước thực hiện
Bước 1: Tạo dự án mới
Tạo project Android Studio mới.
Bước 2: Layout (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".MainActivity">
<Button
android:onClick="createNotification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="16dp"
android:text="Tạo thông báo (có kiểm tra)" />
</RelativeLayout>
Bước 3: MainActivity với kiểm tra (src/MainActivity.java)
package app.tutorialspoint.com.notifyme;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default";
private static final int NOTIFICATION_REQUEST_CODE = 123; // CỐ ĐỊNH
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void createNotification(View view) {
// KIỂM TRA TRƯỚC KHI TẠO
if (isNotificationVisible()) {
Toast.makeText(this, "Thông báo đang hiển thị - không tạo mới", Toast.LENGTH_SHORT).show();
return;
}
// Tạo thông báo mới
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
MainActivity.this, default_notification_channel_id);
mBuilder.setContentTitle("Thông báo có kiểm tra");
mBuilder.setContentText("Nhấn để xem - chỉ cho phép 1 lần");
mBuilder.setSmallIcon(R.drawable.ic_launcher_foreground);
mBuilder.setAutoCancel(true); // Tự hủy khi nhấn
// QUAN TRỌNG: DÙNG REQUEST CODE CỐ ĐỊNH
Intent notificationIntent = new Intent(MainActivity.this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(
MainActivity.this, NOTIFICATION_REQUEST_CODE, notificationIntent,
PendingIntent.FLAG_IMMUTABLE);
mBuilder.setContentIntent(contentIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel notificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "Kênh Kiểm Tra", importance);
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(notificationChannel);
}
assert mNotificationManager != null;
mNotificationManager.notify(NOTIFICATION_REQUEST_CODE, mBuilder.build()); // ID CỐ ĐỊNH
Toast.makeText(this, "Đã tạo thông báo", Toast.LENGTH_SHORT).show();
}
// KIỂM TRA THÔNG BÁO CÓ ĐANG HIỂN THỊ
private boolean isNotificationVisible() {
Intent notificationIntent = new Intent(MainActivity.this, MainActivity.class);
// FLAG_NO_CREATE: Chỉ kiểm tra, KHÔNG tạo mới nếu chưa có
PendingIntent test = PendingIntent.getActivity(
MainActivity.this, NOTIFICATION_REQUEST_CODE, notificationIntent,
PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE);
// Nếu test != null → PendingIntent tồn tại → Notification đang active
// Nếu test == null → PendingIntent đã bị cancel → Notification đã.dismiss
return test != null;
}
}
Bước 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">
<uses-permission android:name="android.permission.VIBRATE" />
<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>
Giải thích cơ chế FLAG_NO_CREATE
Cách hoạt động
// Tạo PendingIntent bình thường (đăng ký với hệ thống)
PendingIntent pi = PendingIntent.getActivity(context, REQUEST_CODE, intent, FLAG_IMMUTABLE);
// Kiểm tra tồn tại (KHÔNG tạo mới)
PendingIntent check = PendingIntent.getActivity(context, REQUEST_CODE, intent, FLAG_NO_CREATE | FLAG_IMMUTABLE);
if (check != null) {
// PendingIntent tồn tại → Notification chưa bị dismiss
// Có thể dùng check.cancel() để hủy nó
} else {
// PendingIntent không tồn tại → Notification đã bị dismiss hoặc chưa tạo
}
Quan trọng: Request Code phải cố định
- Dùng cùng một
requestCode(ví dụ: 123) cho cả tạo và kiểm tra - Dùng cùng
notificationIdkhi gọinotify() FLAG_IMMUTABLEbắt buộc từ Android 12+ (API 31+)
Các trường hợp PendingIntent bị hủy (trả về null)
- User vuốt dismiss thông báo (swipe away)
- User nhấn vào thông báo (với
setAutoCancel(true)) - App gọi
notificationManager.cancel(id) - App gọi
pendingIntent.cancel() - Hệ thống xóa do bộ nhớ thấp (hiếm)
- Device reboot (PendingIntent không persist qua reboot)
Cách khác: NotificationListenerService (API 18+)
Để theo dõi TẤT CẢ thông báo (kể cả của app khác), dùng NotificationListenerService:
public class MyNotificationListener extends NotificationListenerService {
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
// Thông báo mới xuất hiện
String packageName = sbn.getPackageName();
Notification notification = sbn.getNotification();
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
// Thông báo bị xóa
}
}
// Manifest
<service android:name=".MyNotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
// User phải bật trong Settings → Apps → Special access → Notification access
Chạy thử nghiệm
- Chạy app → Nhấn nút → Tạo thông báo
- Nhấn nút lần 2 → Toast "Thông báo đang hiển thị - không tạo mới"
- Vuốt xóa thông báo trên shade
- Nhấn nút lần 3 → Tạo thông báo mới thành công
