Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีเพิ่ม Notification ใหม่เมื่อรับ Push Notification ใน Android

ภาพรวม

ตัวอย่างนี้สาธิตการสร้าง Notification เพิ่มเติมเมื่อรับ Push Notification (FCM) โดยสร้าง Notification ใหม่ใน onMessageReceived() ของ FirebaseMessagingService

ขั้นตอนที่ 1: สร้างโปรเจกต์และเพิ่ม Firebase

// build.gradle (Project)
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.4.0'
    }
}

// build.gradle (App)
apply plugin: 'com.google.gms.google-services'

dependencies {
    implementation 'com.google.firebase:firebase-messaging:23.0.0'
    implementation 'com.google.firebase:firebase-analytics:21.0.0'
}

ขั้นตอนที่ 2: 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="Create notification" />
</RelativeLayout>

ขั้นตอนที่ 3: MainActivity - สร้าง Notification ทดสอบ

package app.tutorialspoint.com.notifyme;

import android.app.NotificationChannel;
import android.app.NotificationManager;
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";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        onNewIntent(getIntent());
    }

    public void createNotification(View view) {
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(MainActivity.this, default_notification_channel_id);

        mBuilder.setContentTitle("Notify Me");
        mBuilder.setContentText("Something important!");
        mBuilder.setSmallIcon(R.drawable.ic_launcher_foreground);
        mBuilder.setAutoCancel(true);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(
                NOTIFICATION_CHANNEL_ID, "NOTIFICATION_CHANNEL_NAME", importance);
            mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
            assert mNotificationManager != null;
            mNotificationManager.createNotificationChannel(notificationChannel);
        }

        assert mNotificationManager != null;
        mNotificationManager.notify((int) System.currentTimeMillis(), mBuilder.build());
    }
}

ขั้นตอนที่ 4: FirebaseMessagingService - สร้าง Notification เพิ่มเมื่อรับ Push

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.support.v4.app.NotificationCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class MyFirebaseMessagingService extends FirebaseMessagingService {
    public static final String NOTIFICATION_CHANNEL_ID = "10001";
    private final static String default_notification_channel_id = "default";

    @Override
    public void onNewToken(String token) {
        super.onNewToken(token);
        // ส่ง Token ไปยัง Server ของคุณ
        sendTokenToServer(token);
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

        // 1. จัดการ Data Message
        if (remoteMessage.getData().size() > 0) {
            handleDataMessage(remoteMessage.getData());
        }

        // 2. จัดการ Notification Message (เมื่อแอปอยู่ Foreground)
        if (remoteMessage.getNotification() != null) {
            String title = remoteMessage.getNotification().getTitle();
            String body = remoteMessage.getNotification().getBody();
            createAdditionalNotification(title, body, remoteMessage.getData());
        }
    }

    private void handleDataMessage(Map data) {
        // ประมวลผลข้อมูลจาก Server
        String customKey = data.get("custom_key");
        // TODO: บันทึกลง DB, อัปเดต UI, ฯลฯ
    }

    private void createAdditionalNotification(String title, String body, Map data) {
        Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
        
        // ส่งข้อมูลจาก Push ไปยัง Activity
        if (data != null) {
            for (Map.Entry entry : data.entrySet()) {
                notificationIntent.putExtra(entry.getKey(), entry.getValue());
            }
        }
        notificationIntent.putExtra("fromPush", true);
        notificationIntent.putExtra("pushTitle", title);
        notificationIntent.putExtra("pushBody", body);
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

        PendingIntent pendingIntent = PendingIntent.getActivity(
            getApplicationContext(), 0, notificationIntent,
            PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT
        );

        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
            getApplicationContext(), default_notification_channel_id)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle(title != null ? title : "New Notification")
            .setContentText(body != null ? body : "You have a new message")
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setCategory(NotificationCompat.CATEGORY_MESSAGE);

        // ใช้ BigTextStyle หากข้อความยาว
        if (body != null && body.length() > 100) {
            mBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(body));
        }

        NotificationManager mNotificationManager = (NotificationManager) 
            getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(
                NOTIFICATION_CHANNEL_ID, "Push Notifications", importance);
            // ตั้งค่าเสียง, ไฟ, สั่น ที่ Channel Level
            notificationChannel.setSound(null, null); // ใช้เสียงเริ่มต้น
            notificationChannel.enableVibration(true);
            notificationChannel.setVibrationPattern(new long[]{0, 500, 200, 500});
            
            mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
            assert mNotificationManager != null;
            mNotificationManager.createNotificationChannel(notificationChannel);
        }

        assert mNotificationManager != null;
        // ใช้ ID ที่ไม่ซ้ำกัน (timestamp) เพื่อไม่ให้ทับ Notification เดิม
        int notificationId = (int) System.currentTimeMillis();
        mNotificationManager.notify(notificationId, mBuilder.build());
    }

    private void sendTokenToServer(String token) {
        // TODO: Implement server token registration
    }
}

ขั้นตอนที่ 5: 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.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <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>

        <!-- Firebase Messaging Service -->
        <service
            android:name=".MyFirebaseMessagingService"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
    </application>
</manifest>

ประเภท Push Notification และการจัดการ

ประเภทBackgroundForegroundonMessageReceived
Notification Messageระบบแสดงอัตโนมัติ✅ เรียกไม่ได้รับ Notification object
Data Message✅ เรียก✅ เรียกได้รับ Data payload ทั้งหมด
Both (Notification + Data)ระบบแสดง Notification✅ เรียกได้รับ Data payload

แนวทางปฏิบัติที่ดี

  1. ใช้ Data Message สำหรับควบคุม Notification เองทั้ง Background และ Foreground
  2. ID ไม่ซ้ำกัน ใช้ System.currentTimeMillis() หรือ atomic counter
  3. Group Notification ใช้ setGroup() และ setGroupSummary() รวม Notification เดียวกัน
  4. จัดการ Token Refresh ใน onNewToken() ส่งไป Server ทันที
  5. Background Execution Limits Android 8+ จำกัด Service ใช้ WorkManager สำหรับงานหนัก

การจัดกลุ่ม Notification (Notification Grouping)

// สร้าง Group Summary
NotificationCompat.Builder summaryBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("ข้อความใหม่ 3 รายการ")
    .setContentText("กดเพื่อดูทั้งหมด")
    .setSmallIcon(R.drawable.ic_launcher_foreground)
    .setGroup("group_key_messages")
    .setGroupSummary(true)
    .setStyle(new NotificationCompat.InboxStyle()
        .addLine("สมชาย: สวัสดี")
        .addLine("มานี: ไปไหนมา")
        .addLine("สมหญิง: อร่อยไหม")
        .setBigContentTitle("3 ข้อความใหม่")
        .setSummaryText("กดเพื่อเปิดแชท"));

notificationManager.notify(GROUP_SUMMARY_ID, summaryBuilder.build());

// Notification ย่อยในกลุ่ม
for (Message msg : messages) {
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setContentTitle(msg.getSender())
        .setContentText(msg.getText())
        .setSmallIcon(R.drawable.ic_launcher_foreground)
        .setGroup("group_key_messages")
        .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN);
    notificationManager.notify(msg.getId(), builder.build());
}