איך מקבלים הודעות באפליקציה ל-Android

ההתראות מ-Firebase פועלות באופן שונה בהתאם למצב של האפליקציה המקבלת – בחזית או ברקע. אם רוצים שאפליקציות בחזית יקבלו הודעות התראה או הודעות נתונים, צריך לכתוב קוד לטיפול בקריאה החוזרת (callback) של onMessageReceived. הסבר על ההבדל בין הודעות התראה להודעות נתונים מופיע בקטע סוגי הודעות.

טיפול בהודעות

כדי לקבל הודעות, צריך להשתמש בשירות שמרחיב את FirebaseMessagingService. השירות צריך לשנות את הערכים של פונקציות ה-callbacks‏ onMessageReceived ו-onDeletedMessages.

חלון הזמן לטיפול בהודעה עשוי להיות קצר מ-20 שניות, בהתאם לעיכובים שנצברו לפני הקריאה ל-onMessageReceived, כולל עיכובים במערכת ההפעלה, זמן ההפעלה של האפליקציה, החסימה של ה-thread הראשי על ידי פעולות אחרות או זמן ארוך מדי של קריאות קודמות ל-onMessageReceived. אחרי הזמן הזה, התנהגויות שונות של מערכת ההפעלה, כמו הפסקת תהליכים ב-Android או מגבלות על ביצוע פעולות ברקע ב-Android O, עלולות להפריע לכם להשלים את העבודה.

השדה onMessageReceived מופיע ברוב סוגי ההודעות, עם החרגות הבאות:

  • הודעות התראה שנשלחות כשהאפליקציה פועלת ברקע. במקרה כזה, ההתראה תישלח למגש המערכת של המכשיר. כשמשתמש מקייש על התראה, מרכז האפליקציות נפתח כברירת מחדל.

  • הודעות עם נתוני עומס ושימוש (payload) של התראה ונתונים, כשהן מתקבלות ברקע. במקרה כזה, ההתראה תישלח לסרגל המערכת של המכשיר, ועומס הנתונים יישלח ב-extras של ה-intent של הפעילות של מרכז האפליקציות.

בקצרה:

מצב האפליקציה התראה נתונים שניהם
חזית onMessageReceived onMessageReceived onMessageReceived
רקע מגש המערכת onMessageReceived התראה: מגש המערכת
נתונים: ב-extras של ה-intent.
למידע נוסף על סוגי ההודעות, ראו התראות והודעות נתונים.

עריכת המניפסט של האפליקציה

כדי להשתמש ב-FirebaseMessagingService, צריך להוסיף את הקטע הבא למניפסט של האפליקציה:

<service
    android:name=".java.MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

בנוסף, מומלץ להגדיר ערכי ברירת מחדל כדי להתאים אישית את המראה של ההתראות. אפשר לציין סמל ברירת מחדל וצבע ברירת מחדל בהתאמה אישית, שיחולו בכל פעם שלא מוגדרים ערכים מקבילים בתוכן של ההתראה.

מוסיפים את השורות הבאות בתוך התג application כדי להגדיר את סמל ברירת המחדל והצבע בהתאמה אישית:

<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
     See README(https://goo.gl/l4GJaQ) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
     notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/colorAccent" />

ב-Android מוצג סמל ברירת המחדל המותאם אישית של

  • כל הודעות ההתראות שנשלחות מ כלי היצירה של התראות.
  • כל הודעת התראה שלא מגדירה את הסמל באופן מפורש במטען הייעודי (payload) של ההתראה.

Android משתמש בצבע ברירת המחדל המותאם אישית עבור:

  • כל הודעות ההתראות שנשלחות מ כלי היצירה של התראות.
  • כל הודעת התראה שלא מגדירה את הצבע באופן מפורש במטען הייעודי (payload) של ההתראה.

אם לא מגדירים סמל ברירת מחדל מותאם אישית ולא מגדירים סמל במטען הייעודי של ההתראה, מערכת Android מציגה את סמל האפליקציה בצבעים לבנים.

שינוי מברירת המחדל של onMessageReceived

שינוי ברירת המחדל של השיטה FirebaseMessagingService.onMessageReceived מאפשר לבצע פעולות על סמך אובייקט RemoteMessage שהתקבל, ולקבל את נתוני ההודעה:

Kotlin+KTX

override fun onMessageReceived(remoteMessage: RemoteMessage) {
    // TODO(developer): Handle FCM messages here.
    // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
    Log.d(TAG, "From: ${remoteMessage.from}")

    // Check if message contains a data payload.
    if (remoteMessage.data.isNotEmpty()) {
        Log.d(TAG, "Message data payload: ${remoteMessage.data}")

        // Check if data needs to be processed by long running job
        if (needsToBeScheduled()) {
            // For long-running tasks (10 seconds or more) use WorkManager.
            scheduleJob()
        } else {
            // Handle message within 10 seconds
            handleNow()
        }
    }

    // Check if message contains a notification payload.
    remoteMessage.notification?.let {
        Log.d(TAG, "Message Notification Body: ${it.body}")
    }

    // Also if you intend on generating your own notifications as a result of a received FCM
    // message, here is where that should be initiated. See sendNotification method below.
}

Java

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    // TODO(developer): Handle FCM messages here.
    // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
    Log.d(TAG, "From: " + remoteMessage.getFrom());

    // Check if message contains a data payload.
    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());

        if (/* Check if data needs to be processed by long running job */ true) {
            // For long-running tasks (10 seconds or more) use WorkManager.
            scheduleJob();
        } else {
            // Handle message within 10 seconds
            handleNow();
        }

    }

    // Check if message contains a notification payload.
    if (remoteMessage.getNotification() != null) {
        Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
    }

    // Also if you intend on generating your own notifications as a result of a received FCM
    // message, here is where that should be initiated. See sendNotification method below.
}

שינוי מברירת המחדל של onDeletedMessages

יש מצבים שבהם FCM לא יכול להעביר הודעה. המצב הזה מתרחש כשיש יותר מדי הודעות (יותר מ-100) בהמתנה באפליקציה במכשיר מסוים בזמן החיבור, או אם המכשיר לא היה מחובר ל-FCM במשך יותר מחודש. במקרים כאלה, יכול להיות שתקבלו קריאה חוזרת ל-FirebaseMessagingService.onDeletedMessages(). כשמכונה של אפליקציה מקבלת את הקריאה החוזרת הזו, היא צריכה לבצע סנכרון מלא עם שרת האפליקציה. אם לא שלחתם הודעה לאפליקציה במכשיר הזה ב-4 השבועות האחרונים, FCM לא יתקשר אל onDeletedMessages().

טיפול בהודעות התראה באפליקציה שפועלת ברקע

כשהאפליקציה פועלת ברקע, מערכת Android מפנה את הודעות ההתראות למגש המערכת. כשמשתמש מקייש על ההתראה, מרכז האפליקציות נפתח כברירת מחדל.

הנתונים האלה כוללים הודעות שמכילות גם הודעה וגם עומס נתונים (וכל ההודעות שנשלחות ממסוף ההתראות). במקרים כאלה, ההתראה מועברת לסרגל המערכת של המכשיר, ועומס הנתונים מועבר ב-extras של ה-intent של הפעילות של מרכז האפליקציות.

כדי לקבל תובנות לגבי העברת ההודעות לאפליקציה, אפשר לעיין ב לוח הבקרה של הדוחות FCM, שבו מתועד מספר ההודעות שנשלחו ונפתחו במכשירי Apple ו-Android, וגם נתונים לגבי 'חשיפות' (התראות שמוצגות למשתמשים) באפליקציות ל-Android.

קבלת הודעות FCM במצב הפעלה ישיר

מפתחים שרוצים לשלוח הודעות FCM לאפליקציות עוד לפני שהמכשיר נעול יכולים לאפשר לאפליקציה ל-Android לקבל הודעות כשהמכשיר נמצא במצב הפעלה ישירה. לדוגמה, יכול להיות שתרצו שמשתמשי האפליקציה יקבלו התראות על אזעקות גם במכשיר נעול.

כשמפתחים את התרחיש לדוגמה הזה, צריך לפעול לפי השיטות המומלצות וההגבלות הכלליות לגבי מצב אתחול ישיר. חשוב במיוחד להביא בחשבון את החשיפה של הודעות שמופעלת בהן הפעלה ישירה. כל משתמש שיש לו גישה למכשיר יכול לראות את ההודעות האלה בלי להזין את פרטי הכניסה של המשתמש.

דרישות מוקדמות

  • צריך להגדיר את המכשיר למצב הפעלה ישיר.
  • במכשיר צריכה להיות מותקנת גרסה עדכנית של Google Play Services (19.0.54 ואילך).
  • כדי לקבל הודעות מ-FCM, האפליקציה צריכה להשתמש ב-FCM SDK‏ (com.google.firebase:firebase-messaging).

הפעלת טיפול בהודעות במצב הפעלה ישיר באפליקציה

  1. בקובץ Gradle ברמת האפליקציה, מוסיפים תלות בספריית התמיכה בהפעלה ישירה של FCM:

    implementation 'com.google.firebase:firebase-messaging-directboot:20.2.0'
    
  2. כדי להפוך את האפליקציה למודעת הפעלה ישירה של FirebaseMessagingService, מוסיפים את המאפיין android:directBootAware="true" למניפסט של האפליקציה:

    <service
        android:name=".java.MyFirebaseMessagingService"
        android:exported="false"
        android:directBootAware="true">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>
    

חשוב לוודא ש-FirebaseMessagingService הזה יכול לפעול במצב אתחול ישיר. צריך לעמוד בדרישות הבאות:

  • השירות לא אמור לגשת לאחסון המוגן באמצעות פרטי הכניסה בזמן שהוא פועל במצב הפעלה ישיר.
  • אסור לשירות לנסות להשתמש ברכיבים כמו Activities,‏ BroadcastReceivers או Services אחרים שלא מסומנים כרכיבים שתומכים בהפעלה ישירה, בזמן שהוא פועל במצב הפעלה ישירה.
  • בנוסף, אסור לספריות שבהן השירות משתמש לגשת לאחסון שמוגן באמצעות פרטי כניסה, או לבצע קריאה לרכיבים שלא תומכים ב-directBootAware במהלך ההפעלה במצב הפעלה ישירה. כלומר, כל הספריות שבהן האפליקציה משתמשת ומתבצעת קריאה אליהן מהשירות צריכות להיות מותאמות לטעינה ישירה, או שהאפליקציה צריכה לבדוק אם היא פועלת במצב טעינה ישירה ולא לבצע קריאה אליהן במצב הזה. לדוגמה, ערכות ה-SDK של Firebase פועלות עם הפעלה ישירה (אפשר לכלול אותן באפליקציה בלי לגרום לקריסה שלה במצב הפעלה ישירה), אבל ממשקי API רבים של Firebase לא תומכים בקריאה במצב הפעלה ישירה.
  • אם האפליקציה משתמשת ב-Application בהתאמה אישית, ה-Application צריך להיות תומך גם בהפעלה ישירה (אין גישה לאחסון שמוגן בפרטי כניסה במצב הפעלה ישירה).

להנחיות לשליחת הודעות למכשירים במצב הפעלה ישירה, ראו שליחת הודעות שתומכות בהפעלה ישירה.