אם אתם משתמשים בממשקי API של FCM כדי ליצור בקשות שליחה באופן פרוגרמטי, יכול להיות שתגלו שעם הזמן אתם מבזבזים משאבים על שליחת הודעות למכשירים לא פעילים עם רישומים מיושנים. המצב הזה יכול להשפיע על נתוני מסירת ההודעות שמדווחים במסוף Firebase או על הנתונים שמיוצאים אל BigQuery, ולהופיע כירידה דרמטית (אבל לא חוקית בפועל) בשיעורי המסירה. במדריך הזה נסביר על כמה פעולות שאפשר לבצע כדי לוודא שההודעות מטורגטות ביעילות ושהדיווח על המסירה תקין.
רישומים לא עדכניים ורישומים שפג תוקפם
רישומים לא עדכניים משויכים למכשירים לא פעילים שלא התחברו אל FCM במשך יותר מחודש. ככל שעובר הזמן, הסיכוי שהמכשיר יתחבר שוב אל FCM הולך ופוחת. סביר להניח שהודעות שנשלחות ונושאים שמופצים לרישומים הישנים האלה לא יגיעו ליעד.
יש כמה סיבות לכך שרישום יכול להתיישן. לדוגמה, יכול להיות שהמכשיר שהרישום משויך אליו אבד, נהרס או הועבר לאחסון ונשכח.
ב-Android, אם רישום לא פעיל במשך 270 ימים, FCM מחשיב אותו כפג תוקף ומנקה אותו. אחרי שהרישום פג, FCM מסמן אותו כלא חוקי ודוחה שליחה אליו. שימו לב שמזהי ההתקנה של Firebase (FID) מנוהלים על ידי שירות ההתקנות של Firebase (FIS), ולא על ידי FCM. במקרים הנדירים שבהם מכשיר מתחבר שוב והאפליקציה נפתחת אחרי שהרישום שלה נאסף כזבל, אפליקציית הלקוח נרשמת שוב ב-FCM באמצעות ה-FID שאוחזר מ-FIS. שימו לב שה-FID עשוי להשתנות. בניהול התקנות של Firebase מפורט מתי מנפיקים מחדש מזהי FID.
בפלטפורמות אחרות כמו iOS, FCM מסתמך על שירות ההתראות הבסיסי (למשל APNs), שבו אין תפוגה זהה של 270 ימים על סמך חוסר פעילות. מומלץ לשמור על עדכניות הרישום ולהסיר רישומים לא עדכניים.
שיטות מומלצות בסיסיות
יש כמה שיטות בסיסיות שצריך לפעול לפיהן בכל אפליקציה שמשתמשת בממשקי API של FCM כדי ליצור בקשות שליחה באופן פרוגרמטי. השיטות המומלצות העיקריות הן:
- מאחזרים את מזהי ההתקנה של Firebase (FID) מ-FCM ומאחסנים אותם בשרת האפליקציה. אחד מהתפקידים החשובים של השרת הוא לעקוב אחרי מזהה ה-FID הרשום של כל לקוח ולנהל רשימה מעודכנת של מזהי FID פעילים. מומלץ מאוד להטמיע חותמת זמן של רישום במסד הנתונים, ולעדכן אותה בכל פעם שמעלים רישום.
- לשמור על עדכניות הרישום ולהסיר רישומים לא עדכניים. בנוסף להסרת רישומים ש-FCM כבר לא מחשיב כתקפים, כדאי לעקוב אחרי סימנים אחרים לכך שהרישומים כבר לא עדכניים ולהסיר אותם באופן יזום. במדריך הזה נסביר על כמה מהאפשרויות להשגת המטרה הזו.
אחזור ואחסון של מזהי ההתקנה של Firebase
בפעם הראשונה שהאפליקציה מופעלת, ה-SDK של FCM רושם את מופע האפליקציה ב-FCM ומחזיר מזהה התקנה של Firebase (FID). זהו המזהה שצריך לכלול בבקשות שליחה ממוקדות מה-API, או להשתמש בו למינויים לנושאים.
מומלץ מאוד לשמור את ה-FID בשרת האפליקציה לצד חותמת זמן בכל פעם שהוא מועלה. כשמעדכנים את חותמת הזמן בכל בקשת העלאה, השרת יודע מתי מופע האפליקציה נפתח לאחרונה וסונכרן בהצלחה עם הקצה העורפי של FCM.
בהתאם להפעלה או להשבתה של ההפעלה האוטומטית (כולל אם היא לא נתמכת), צריך לטפל ברישום ובעדכונים באופן הבא:
- (מומלץ) כשההפעלה האוטומטית מופעלת: ה-SDK מעדכן באופן אוטומטי את הרישום ועוקב אחרי שינויים. הקריאה החוזרת (callback)
onRegistered()מופעלת באופן קבוע בסנכרונים שגרתיים במהלך הפעלת האפליקציה, וגם כשמתרחשים שינויים ב-FID. פשוט מטמיעים את הקריאה החוזרת הזו כדי להעלות את ה-FID לשרת ולשמור את חותמת הזמן הנוכחית. - כשההפעלה האוטומטית מושבתת: הקריאה החוזרת (callback) של
onRegistered()לא תופעל באופן אוטומטי בהתחלה. כדי לעקוב אחרי הרישומים ולשמור אותם עדכניים, צריך להפעיל אתregister()בזמן הפעלת האפליקציה. לדוגמה, ב-Android, ב-onCreate()של הפעילות הראשית. קריאה מוצלחת מפעילה את תהליך הרישום של FCM באמצעות ה-FID ומעבירה אותו לקריאה החוזרת (callback) שלonRegistered(), וכך האפליקציה יכולה להעלות את ה-FID ולעדכן את חותמת הזמן בשרת.
דוגמה: אחסון מזהי FID וחותמות זמן ב-Cloud Firestore
לדוגמה, אפשר להשתמש ב-Cloud Firestore כדי לאחסן מזהי FID באוסף שנקרא fcmRegistrations. כל מזהה מסמך באוסף תואם למזהה משתמש, והמסמך מאחסן את ה-FID הנוכחי ואת חותמת הזמן של העדכון האחרון שלו. משתמשים בפונקציה set, כמו בדוגמה הזו ב-Kotlin:
private fun sendRegistrationToServer(installationId: String?) {
// If you're running your own server, call API to send registration details and today's date for the user
// Example shown uses Firestore
// Add FID and timestamp to Firestore for this user
val deviceFid = hashMapOf(
"installationId" to installationId,
"timestamp" to FieldValue.serverTimestamp(),
)
// Get user ID from Firebase Auth or your own server
Firebase.firestore.collection("fcmRegistrations").document("myuserid")
.set(deviceFid)
}
בכל פעם שמזהה התקנה של Firebase נרשם או מתעדכן, מופעלת פונקציית ה-callback onRegistered(). צריך להטמיע את הקריאה החוזרת הזו כדי להעלות את ה-FID ולעדכן את חותמת הזמן:
override fun onRegistered(installationId: String) {
Log.d(TAG, "Registered installation ID: $installationId")
// Send the Firebase Installation ID (FID) to your app server. Your app
// server should save the FID and update the timestamp upon receipt.
sendRegistrationToServer(installationId)
}
במקרים שבהם ההפעלה האוטומטית מושבתת, צריך להפעיל את register() בזמן הפעלת האפליקציה (למשל ב-onCreate()) כדי להפעיל את תהליך הרישום ואת שליחת ה-FID דרך onRegistered():
// Trigger manual registration if auto-initialization is turned off.
FirebaseMessaging.getInstance().register()
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// The registration callback onRegistered() will be invoked with the current FID.
} else {
Log.w(TAG, "Failed to register with Firebase Cloud Messaging", task.exception)
}
}
שמירה על עדכניות הרישום והסרת רישומים לא עדכניים
לא תמיד קל לקבוע אם רישום הוא עדכני או לא. כדי לכסות את כל המקרים, כדאי לקבוע סף שמעבר לו הרישומים נחשבים למיושנים. כברירת מחדל, FCM מחשיב רישום כמיושן אם מופע האפליקציה שלו לא התחבר במשך חודש. כל רישום שבוצע לפני יותר מחודש הוא כנראה של מכשיר לא פעיל. אחרת, הרישום של המכשיר הפעיל היה מתרענן.
בהתאם לתרחיש לדוגמה, יכול להיות שחודש אחד יהיה קצר מדי או ארוך מדי, ולכן אתם צריכים לקבוע את הקריטריונים שמתאימים לכם.
זיהוי תגובות לא תקפות מה-backend של FCM
חשוב לזהות תגובות לא תקפות מ-FCM ולהגיב על ידי מחיקה מהמערכת של כל הרישומים שידוע שהם לא תקפים או שפג תוקפם. ב-HTTP v1 API, הודעות השגיאה האלה עשויות להצביע על כך שבקשת השליחה שלכם טירגטה רישומים לא חוקיים או שפג תוקפם:
UNREGISTERED(HTTP 404)INVALID_ARGUMENT(HTTP 400)
אם אתם בטוחים שהמטען הייעודי (payload) של ההודעה תקין ואתם מקבלים את אחת מהתגובות האלה לרישום ממוקד, אתם יכולים למחוק את הרשומה של הרישום הזה, כי הוא לא יהיה תקין יותר. לדוגמה, כדי למחוק רישומים לא חוקיים מ-Cloud Firestore, אפשר לפרוס ולהריץ פונקציה כמו זו:
// Firebase Installation ID comes from the client FCM SDKs
const firebaseInstallationId = 'YOUR_FIREBASE_INSTALLATION_ID';
const message = {
data: {
// Information you want to send inside of notification
},
fid: firebaseInstallationId
};
// Send message to device with provided Firebase Installation ID
getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
})
.catch((error) => {
// Delete registration for user if error code is UNREGISTERED or INVALID_ARGUMENT.
if (error.errorCode == "messaging/registration-token-not-registered") {
// If you're running your own server, call API to delete the registration for the user
// Example shown uses Firestore
// Get user ID from Firebase Auth or your own server
Firebase.firestore.collection("fcmRegistrations").document(user.uid).delete()
}
});
FCM מחזיר תגובה לא תקפה אם הרישום של מכשיר Android פג אחרי 270 ימים של חוסר פעילות, או אם לקוח ביטל את הרישום באופן מפורש. אם אתם צריכים לעקוב בצורה מדויקת יותר אחרי רישומים לא עדכניים לפי ההגדרות שלכם, אתם יכולים להסיר רישומים לא עדכניים באופן יזום.
עדכון הרישומים באופן קבוע
לא משנה אם הרישומים מבוססים על מזהי FID או על אסימוני רישום מדור קודם, השרת תמיד צריך לעדכן את חותמת הזמן של הרישום במסד הנתונים בכל בקשת העלאה. חותמת הזמן הזו משמשת כאות להתקנת האפליקציה, ומאפשרת ללקוח לפתוח את האפליקציה בהצלחה ולסנכרן אותה עם הקצה העורפי של FCM. בהתאם לממשקי ה-API שבהם אתם משתמשים, אתם צריכים להטמיע את האסטרטגיה המתאימה:
ממשקי ה-API של מזהה ההתקנה של Firebase (מומלץ)
באפליקציות לקוח שמשתמשות בממשקי ה-API של FID, אין צורך לתזמן משימות תקופתיות ברקע כדי לאחזר או לרענן את הרישומים. ה-SDK מטפל באופן אוטומטי ברענונים במסגרת האתחול האוטומטי, ומעביר באופן קבוע את ה-FID הנוכחי הנכון לקריאה החוזרת (callback) של onRegistered() בסנכרונים שגרתיים במהלך הפעלות של האפליקציה.
כדי שהשרת יישאר מעודכן, צריך להטמיע את אסטרטגיות ההעלאה בזמן ההפעלה שמפורטות במאמר אחזור ואחסון של מזהי התקנות של Firebase:
- הפעלה אוטומטית: ה-SDK מוודא באופן אוטומטי שמזהה ה-FID העדכני ביותר נשלח לשרת שלכם בסנכרונים שגרתיים בזמן הפעלת האפליקציה.
- ההפעלה האוטומטית מושבתת או לא נתמכת: צריך להפעיל את
register()בזמן הפעלת האפליקציה (לדוגמה, ב-Android, בפעילות הראשיתonCreate()) כדי לאלץ את רצף הרישום ולהפעיל את העברת ה-FID אל פונקציית ה-callback שלonRegistered().
האסטרטגיות האלה מבטיחות שבשרת תמיד יהיה ה-FID הפעיל העדכני ביותר, ושהוא יוכל להתאושש מהעלאות שנכשלו באופן אוטומטי, מה שהופך את האפליקציה לעמידה מאוד.
ממשקי ה-API של אסימוני הרישום שהוצאו משימוש
אם אתם משתמשים באסימוני רישום מדור קודם, ה-SDK של הלקוח לא מנהל באופן אוטומטי את הרענונים בסנכרונים שגרתיים. לכן מומלץ לאחזר ולעדכן מדי פעם את כל אסימוני הרישום בשרת. כדי לעשות את זה, צריך:
- מוסיפים לוגיקה של אפליקציה באפליקציית הלקוח כדי לאחזר את הטוקן הנוכחי באמצעות הקריאה המתאימה ל-API (למשל
token(completion):לפלטפורמות של אפל אוgetToken()ל-Android), ואז שולחים את הטוקן הנוכחי לשרת האפליקציה לאחסון (עם חותמת זמן). למשל, משימה חודשית שמוגדרת לכלול את כל הלקוחות או הטוקנים. - מוסיפים לוגיקה של שרת כדי לעדכן את חותמת הזמן של הטוקן במרווחי זמן קבועים, גם אם הטוקן לא השתנה.
בפוסט Managing Cloud Messaging Tokens בבלוג של Firebase מופיעה דוגמה ללוגיקה של Android לעדכון טוקנים מדור קודם באמצעות WorkManager.
לא משנה באיזה דפוס תזמון אתם משתמשים, חשוב לעדכן את הטוקנים מדי פעם. תדירות עדכון של פעם בחודש מאזנת היטב בין ההשפעה על הסוללה לבין זיהוי אסימוני רישום לא פעילים. הפעולה הזו גם מבטיחה שכל מכשיר שהופך ללא פעיל ירענן את הרישום שלו כשהוא יהפוך שוב לפעיל. אין טעם לרענן את הנתונים בתדירות גבוהה יותר מפעם בשבוע.
הסרת רישומים לא עדכניים
לפני ששולחים הודעות למכשיר, צריך לוודא שחותמת הזמן של רישום המכשיר נמצאת בטווח של חלון הזמן שנקבע. לדוגמה, אפשר להטמיע את Cloud Functions for Firebase כדי להריץ בדיקה יומית ולוודא שחותמת הזמן נמצאת בטווח זמן מוגדר של חוסר עדכניות, כמו const
EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30;, ואז להסיר רישומים לא עדכניים:
exports.pruneRegistrations = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
// Get all documents where the timestamp exceeds is not within the past month
const staleRegistrationsResult = await admin.firestore().collection('fcmRegistrations')
.where("timestamp", "<", Date.now() - EXPIRATION_TIME)
.get();
// Delete devices with stale registrations
staleRegistrationsResult.forEach(function(doc) { doc.ref.delete(); });
});
ביטול ההרשמה של רישומים לא פעילים לנושאים
אם אתם משתמשים בנושאים, יכול להיות שתרצו גם לבטל את ההרשמה של רישומים לא פעילים מהנושאים שהם רשומים אליהם. התהליך כולל שני שלבים:
- האפליקציה צריכה להירשם מחדש לנושאים בכל פעם שמזהה ההתקנה של Firebase (FID) משתנה. כך המינויים יופיעו שוב באופן אוטומטי כשהאפליקציה תהיה פעילה שוב.
- אם מופע של אפליקציה לא פעיל במשך חודש (או חלון הזמן שאתם הגדרתם), צריך לבטל את ההרשמה שלו לנושאים באמצעות ה-SDK של Firebase לאדמינים כדי למחוק את המיפוי של מזהה ההתקנה של Firebase לנושא מה-backend של FCM.
היתרון של שני השלבים האלה הוא שההפצה תתבצע מהר יותר כי יש פחות רישומים לא פעילים להפיץ אליהם, ומופעי האפליקציה הלא פעילים יירשמו מחדש באופן אוטומטי כשהם יהיו פעילים שוב.
מדידת הצלחת המסירה
כדי לקבל את התמונה המדויקת ביותר של מסירת ההודעות, מומלץ לשלוח הודעות רק למופעי אפליקציה שנמצאים בשימוש פעיל. זה חשוב במיוחד אם אתם שולחים הודעות באופן קבוע לנושאים עם מספר גדול של מנויים. אם חלק מהמנויים האלה לא פעילים, ההשפעה על נתוני המסירה שלכם יכולה להיות משמעותית לאורך זמן.
לפני שמטרגטים הודעות למופע של אפליקציה, כדאי להביא בחשבון את הדברים הבאים:
- האם Google Analytics, נתונים שנאספו ב-BigQuery או אותות מעקב אחרים מצביעים על כך שההרשמה פעילה?
- האם ניסיונות המסירה הקודמים נכשלו באופן עקבי לאורך זמן?
- האם מזהה ההתקנה של Firebase עודכן בשרתים שלכם בחודש האחרון?
- במכשירי Android, האם ב-FCM Data API מדווח על אחוז גבוה של כשלים במסירת הודעות בגלל
droppedDeviceInactive?
למידע נוסף על מסירה, ראו הסבר על מסירת הודעות.