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

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

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

כדי לקבל הודעות, צריך להשתמש בשירות שמרחיב את FirebaseMessagingService. השירות אמור לבטל את הקריאות החוזרות onMessageReceived ו-onDeletedMessages.

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

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

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

  • הודעות עם מטען ייעודי (payload) גם של התראות וגם של מטען ייעודי (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>

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

מוסיפים את השורות הבאות בתוך התג 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(). כשהמופע של האפליקציה מקבל את הקריאה החוזרת (callback) הזה, הוא אמור לבצע סנכרון מלא עם שרת האפליקציות. אם לא שלחתם הודעה לאפליקציה במכשיר הזה ב-4 השבועות האחרונים, FCM לא יתקשר אל onDeletedMessages().

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

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

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

כדי לקבל תובנות לגבי העברת ההודעות לאפליקציה, אפשר לעיין בלוח הבקרה של הדוחות 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 יצטרך להיות עם בקרת גישה ישירה (אין גישה לאחסון מוגן באמצעות פרטי כניסה במצב הפעלה ישירה).

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