Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Kiểm tra thông báo Android có hiển thị hoặc đã hủy không

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 notificationId khi gọi notify()
  • FLAG_IMMUTABLE bắ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

  1. Chạy app → Nhấn nút → Tạo thông báo
  2. Nhấn nút lần 2 → Toast "Thông báo đang hiển thị - không tạo mới"
  3. Vuốt xóa thông báo trên shade
  4. Nhấn nút lần 3 → Tạo thông báo mới thành công

Kiểm tra thông báo Android có hiển thị hoặc đã hủy không