Notification là thông báo hiển thị ngoài ứng dụng (thanh trạng thái, màn hình khóa, shade). NotificationCompat.Builder (AndroidX) đảm bảo tương thích ngược và hỗ trợ tính năng mới (Channel, Action, MediaStyle...).
Yêu cầu Android 8.0+ (API 26): Notification Channel
Bắt buộc tạo NotificationChannel trước khi post notification. Mỗi channel có importance, sound, vibration, light...
Ví dụ cơ bản
1. Dependency
dependencies {
implementation 'androidx.core:core:1.12.0' // Đã có NotificationCompat
}
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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnNotify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Gửi thông báo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
3. MainActivity.java
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
public class MainActivity extends AppCompatActivity {
private static final String CHANNEL_ID = "tutorialspoint_channel";
private static final int NOTIFICATION_ID = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createNotificationChannel();
Button btnNotify = findViewById(R.id.btnNotify);
btnNotify.setOnClickListener(v -> sendNotification());
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Thông báo chung";
String description = "Kênh cho các thông báo cơ bản của app";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
channel.enableLights(true);
channel.setLightColor(Color.BLUE);
channel.enableVibration(true);
channel.setVibrationPattern(new long[]{0, 500, 200, 500});
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
private void sendNotification() {
// Intent khi tap notification
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
// Action button (ví dụ: "Reply")
Intent replyIntent = new Intent(this, ReplyReceiver.class); // BroadcastReceiver
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(
this, 1, replyIntent, PendingIntent.FLAG_IMMUTABLE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification) // Bắt buộc: icon nhỏ (white/transparent bg)
.setContentTitle("Thông báo mới")
.setContentText("Đây là nội dung thông báo mẫu từ Tutorialspoint")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("Nội dung mở rộng dài hơn, hiển thị khi người dùng kéo xuống notification shade."))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true) // Tự dismiss khi tap
.setContentIntent(pendingIntent)
.addAction(R.drawable.ic_reply, "Trả lời", replyPendingIntent)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.setCategory(NotificationCompat.CATEGORY_MESSAGE);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
// Kiểm tra quyền post notification (Android 13+ API 33)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED) {
notificationManager.notify(NOTIFICATION_ID, builder.build());
} else {
// Yêu cầu runtime permission
requestPermissions(new String[]{android.Manifest.permission.POST_NOTIFICATIONS}, 100);
}
} else {
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
}
Các Style Notification phổ biến
BigTextStyle (Nội dung dài)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("Nội dung rất dài..."))
.setStyle(new NotificationCompat.BigTextStyle()
.setBigContentTitle("Tiêu đề mở rộng")
.bigText("Nội dung...")
.setSummaryText("Tóm tắt"))
BigPictureStyle (Ảnh lớn)
.setStyle(new NotificationCompat.BigPictureStyle()
.bigPicture(bitmap)
.bigLargeIcon(null) // Ẩn large icon khi expand
.setSummaryText("Mô tả ảnh"))
InboxStyle (Danh sách items)
.setStyle(new NotificationCompat.InboxStyle()
.addLine("Item 1")
.addLine("Item 2")
.addLine("Item 3")
.setBigContentTitle("3 tin nhắn mới")
.setSummaryText("Từ 2 người"))
MessagingStyle (Chat/Meeting)
Person person = new Person.Builder()
.setName("Nguyễn Văn A")
.setIcon(Icon.createWithResource(this, R.drawable.avatar))
.build();
NotificationCompat.MessagingStyle style = new NotificationCompat.MessagingStyle(person)
.setConversationTitle("Cuộc trò chuyện nhóm")
.addMessage("Chào bạn!", System.currentTimeMillis(), person)
.addMessage("Bạn khỏe không?", System.currentTimeMillis() - 1000, new Person.Builder().setName("B").build());
builder.setStyle(style);
MediaStyle (Media Player)
.setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.getSessionToken())
.setShowActionsInCompactView(0, 1, 2) // Action index hiển thị khi collapse
.setShowCancelButton(true)
.setCancelButtonIntent(cancelPendingIntent))
Quyền POST_NOTIFICATIONS (Android 13+ API 33)
// Runtime request (trong Activity) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, REQ_CODE); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQ_CODE && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { sendNotification(); // Gửi lại sau khi cấp quyền } }
PendingIntent Flags (Quan trọng)
- FLAG_IMMUTABLE: Bắt buộc từ Android 12 (API 31) cho security
- FLAG_MUTABLE: Chỉ dùng khi cần mutate extras (ít gặp)
- FLAG_UPDATE_CURRENT: Cập nhật extra nếu PendingIntent đã tồn tại
- FLAG_CANCEL_CURRENT: Hủy cái cũ, tạo mới
- FLAG_NO_CREATE: Không tạo mới nếu chưa có, trả null
- FLAG_ONE_SHOT: Chỉ dùng một lần (dành cho alarm, notification action)
Hủy Notification
// Hủy một notification cụ thể
notificationManager.cancel(NOTIFICATION_ID);
// Hủy tất cả
notificationManager.cancelAll();
// Hủy theo tag (nếu dùng notify(tag, id, notification))
notificationManager.cancel("tag", NOTIFICATION_ID);
Chạy ứng dụng
Nhấn "Gửi thông báo" → Notification xuất hiện trên status bar. Kéo shade xuống → Xem nội dung mở rộng, nút "Trả lời". Tap notification → Mở MainActivity. Swipe dismiss hoặc tap "Clear all" → Notification biến mất.
Best Practices
- Luôn tạo Channel (API 26+)
- Dùng NotificationManagerCompat cho tương thích
- setSmallIcon bắt buộc, nên là vector white/transparent background
- Priority: HIGH/MAX có heads-up (peeking), DEFAULT trở xuống không
- Group notification cho cùng chủ đề:
setGroup("group_key") - Sử dụng WorkManager/AlarmManager cho notification định kỳ