אם אתם משתמשים ב-FCM APIs כדי ליצור בקשות שליחה באופן פרוגרמטי, יכול להיות שבמהלך הזמן תגלו שאתם מבזבזים משאבים בשליחת הודעות למכשירים לא פעילים עם טוקנים ישנים של רישום. המצב הזה יכול להשפיע על נתוני מסירת ההודעות שמוצגים במסוף Firebase או על נתונים שמיוצאים ל-BigQuery, ולהופיע כירידה דרמטית (אבל לא באמת תקפה) בשיעורי המסירה. במדריך הזה נסביר על כמה אמצעים שאפשר לנקוט כדי להבטיח טירגוט יעיל של הודעות ודיווח תקין על מסירה.
טוקנים של הרשמה שפג תוקפם או שהם לא עדכניים
טוקנים לא עדכניים של רישום הם טוקנים שמשויכים למכשירים לא פעילים שלא התחברו ל-FCM במשך יותר מחודש. ככל שעובר הזמן, הסיכוי שהמכשיר יתחבר שוב ל-FCM הולך ופוחת. סביר להניח שהודעות שנשלחות ושידורים של נושאים באמצעות טוקנים לא פעילים כאלה לא יגיעו ליעד.
יש כמה סיבות לכך שאסימון יכול להיות לא עדכני. לדוגמה, יכול להיות שהמכשיר שאליו משויך האסימון אבד, נהרס או אוחסן ונשכח.
ב-Android, כשמגיעים ל-270 ימים של חוסר פעילות של טוקנים לא עדכניים, המערכת של FCM מחשיבה אותם כלא תקפים. אחרי שתוקף הטוקן פג, מערכת FCM מסמנת אותו כלא תקף ודוחה שליחות אליו. עם זאת, FCM מונפק טוקן חדש למופע האפליקציה במקרה הנדיר שהמכשיר מתחבר מחדש והאפליקציה נפתחת.
בפלטפורמות אחרות כמו iOS, FCM מסתמך על שירות הדחיפה הבסיסי (למשל, APNs), שאין לו את אותו תוקף של טוקן שמבוסס על חוסר פעילות למשך 270 ימים. לכן מומלץ לשמור על טריות הטוקנים ולהסיר טוקנים ישנים של הרשמה.
שיטות מומלצות בסיסיות
יש כמה שיטות בסיסיות שמומלץ לפעול לפיהן בכל אפליקציה שמשתמשת בממשקי FCM API כדי ליצור בקשות שליחה באופן פרוגרמטי. השיטות המומלצות העיקריות הן:
- מאחזרים את אסימוני הרישום מ-FCM ושומרים אותם בשרת. תפקיד חשוב של השרת הוא לעקוב אחרי האסימון של כל לקוח ולשמור רשימה מעודכנת של אסימונים פעילים. מומלץ מאוד להטמיע חותמת זמן של טוקן בקוד ובשרתים, ולעדכן את חותמת הזמן הזו במרווחי זמן קבועים.
- חשוב לשמור על טריות האסימונים ולהסיר אסימונים לא פעילים. בנוסף להסרת טוקנים ש-FCM כבר לא מחשיב כתקינים, כדאי לעקוב אחרי סימנים אחרים לכך שטוקנים הפכו למיושנים ולהסיר אותם באופן יזום. במדריך הזה מפורטות כמה אפשרויות שיעזרו לכם להשיג את המטרה הזו.
אחזור ואחסון של טוקנים של הרשמה
בהפעלה הראשונית של האפליקציה, FCM SDK יוצר אסימון רישום עבור מופע אפליקציית הלקוח. זהו האסימון שצריך לכלול בבקשות לשליחה ממוקדת מ-API, או להוסיף למינויים לנושאים לצורך טירגוט נושאים.
מומלץ מאוד שהאפליקציה תאחזר את הטוקן הזה בהפעלה הראשונית ותשמור אותו בשרת האפליקציה לצד חותמת זמן. חותמת הזמן הזו צריכה להיות מוטמעת בקוד ובשרתים שלכם, כי היא לא מסופקת לכם על ידי ערכות ה-SDK של FCM.
בנוסף, חשוב לשמור את האסימון בשרת ולעדכן את חותמת הזמן בכל פעם שהוא משתנה, למשל במקרים הבאים:
- האפליקציה משוחזרת במכשיר חדש
- המשתמש מסיר את האפליקציה או מתקין אותה מחדש
- המשתמש מנקה את נתוני האפליקציה
- האפליקציה חוזרת להיות פעילה אחרי שפג התוקף של האסימון הקיים של FCM
דוגמה: אחסון של טוקנים וחותמות זמן ב-Cloud Firestore
לדוגמה, אפשר להשתמש ב-Cloud Firestore כדי לאחסן טוקנים באוסף שנקרא fcmTokens. כל מזהה מסמך באוסף תואם למזהה משתמש, והמסמך מאחסן את אסימון ההרשמה הנוכחי ואת חותמת הזמן של העדכון האחרון שלו. משתמשים בפונקציה set כמו בדוגמה הזו של Kotlin:
/**
* Persist token to third-party servers.
*
* Modify this method to associate the user's FCM registration token with any server-side account
* maintained by your application.
*
* @param token The new token.
*/
private fun sendTokenToServer(token: String?) {
// If you're running your own server, call API to send token and today's date for the user
// Example shown below with Firestore
// Add token and timestamp to Firestore for this user
val deviceToken = hashMapOf(
"token" to token,
"timestamp" to FieldValue.serverTimestamp(),
)
// Get user ID from Firebase Auth or your own server
Firebase.firestore.collection("fcmTokens").document("myuserid")
.set(deviceToken)
}
בכל פעם שטוקן מאוחזר, הוא מאוחסן ב-Cloud Firestore על ידי קריאה ל-sendTokenToServer:
/**
* Called if the FCM registration token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the
* FCM registration token is initially generated so this is where you would retrieve the token.
*/
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// FCM registration token to your app server.
sendTokenToServer(token)
}
var token = Firebase.messaging.token.await()
// Check whether the retrieved token matches the one on your server for this user's device
val preferences = this.getPreferences(Context.MODE_PRIVATE)
val tokenStored = preferences.getString("deviceToken", "")
lifecycleScope.launch {
if (tokenStored == "" || tokenStored != token)
{
// If you have your own server, call API to send the above token and Date() for this user's device
// Example shown below with Firestore
// Add token and timestamp to Firestore for this user
val deviceToken = hashMapOf(
"token" to token,
"timestamp" to FieldValue.serverTimestamp(),
)
// Get user ID from Firebase Auth or your own server
Firebase.firestore.collection("fcmTokens").document("myuserid")
.set(deviceToken).await()
}
}
שמירה על עדכניות הטוקן והסרת טוקנים לא עדכניים
לא תמיד קל לקבוע אם אסימון הוא חדש או ישן. כדי לכסות את כל המקרים, כדאי להגדיר סף שמעבר לו הטוקנים נחשבים לא עדכניים. כברירת מחדל, FCM מחשיב אסימון כלא עדכני אם מופע האפליקציה שלו לא התחבר במשך חודש. אם חלף יותר מחודש מאז שהונפק טוקן, סביר להניח שמדובר במכשיר לא פעיל. אחרת, הטוקן של מכשיר פעיל היה מתעדכן.
בהתאם לתרחיש השימוש, יכול להיות שחודש אחד יהיה קצר מדי או ארוך מדי, ולכן אתם צריכים להגדיר את הקריטריונים שמתאימים לכם.
זיהוי תגובות לא תקינות של טוקנים מהקצה העורפי של FCM
חשוב לזהות תגובות לא תקינות של טוקנים מ-FCM ולהגיב להן על ידי מחיקה מהמערכת של כל טוקן רישום שידוע שהוא לא תקין או שתוקפו פג. ב-API של HTTP v1, הודעות השגיאה האלה עשויות להצביע על כך שבקשת השליחה שלכם כוונה לטוקנים לא תקינים או לטוקנים שתוקף השימוש בהם פג:
UNREGISTERED(HTTP 404)-
INVALID_ARGUMENT(HTTP 400)
אם אתם בטוחים שהמטען הייעודי של ההודעה תקין ואתם מקבלים אחת מהתגובות האלה עבור טוקן ממוקד, אפשר למחוק את הרשומה של הטוקן הזה, כי הוא לא יהיה תקף יותר. לדוגמה, כדי למחוק טוקנים לא תקינים מ-Cloud Firestore, אפשר לפרוס ולהריץ פונקציה כמו הבאה:
// Registration token comes from the client FCM SDKs
const registrationToken = 'YOUR_REGISTRATION_TOKEN';
const message = {
data: {
// Information you want to send inside of notification
},
token: registrationToken
};
// Send message to device with provided registration token
getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
})
.catch((error) => {
// Delete token 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
token for the user
// Example shown below with Firestore
// Get user ID from Firebase Auth or your own server
Firebase.firestore.collection("fcmTokens").document(user.uid).delete()
}
});
FCM מחזירה תגובה לא חוקית של אסימון אם תוקף האסימון למכשיר Android פג אחרי 270 ימים של חוסר פעילות, או אם לקוח ביטל את הרישום באופן מפורש. אם אתם רוצים לעקוב אחרי מידת העדכניות בצורה מדויקת יותר בהתאם להגדרות שלכם, אתם יכולים להסיר באופן יזום את טוקני הרישום שלא עודכנו.
עדכון אסימונים באופן קבוע
מומלץ לאחזר ולעדכן מעת לעת את כל אסימוני הרישום בשרת. כדי לעשות את זה, אתם צריכים:
- מוסיפים לאפליקציית הלקוח לוגיקה של אפליקציה כדי לאחזר את הטוקן הנוכחי באמצעות קריאת ה-API המתאימה (למשל,
token(completion):לפלטפורמות של אפל אוgetToken()ל-Android), ואז שולחים את הטוקן הנוכחי לשרת האפליקציה לאחסון (עם חותמת זמן). יכול להיות שמדובר בעבודה חודשית שהוגדרה כך שתכסה את כל הלקוחות או האסימונים. - מוסיפים לוגיקה של שרת כדי לעדכן את חותמת הזמן של האסימון במרווחי זמן קבועים, ללא קשר לשאלה אם האסימון השתנה או לא.
דוגמה ללוגיקה של Android לעדכון טוקנים באמצעות WorkManager מופיעה במאמר ניהול טוקנים של Cloud Messaging בבלוג של Firebase.
לא משנה באיזה דפוס עיתוי תבחרו, חשוב לעדכן את האסימונים באופן קבוע. תדירות עדכון של פעם בחודש מאפשרת איזון טוב בין ההשפעה על הסוללה לבין זיהוי של טוקנים לא פעילים של רישום. בנוסף, הרענון הזה מבטיח שמכשיר שהפסיק להיות פעיל ירענן את הרישום שלו כשהוא יהפוך שוב לפעיל. אין יתרון לרענון בתדירות גבוהה יותר משבועית.
הסרת טוקנים לא עדכניים של הרשמה
לפני ששולחים הודעות למכשיר, צריך לוודא שחותמת הזמן של טוקן הרישום של המכשיר נמצאת בתוך חלון הזמן של תקופת ההתיישנות. לדוגמה, אפשר להטמיע את Cloud Functions for Firebase כדי להפעיל בדיקה יומית ולוודא שחותמת הזמן נמצאת בתוך חלון זמן מוגדר של נתונים לא עדכניים, כמו const
EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30;, ואז להסיר את האסימונים הלא עדכניים:
exports.pruneTokens = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
// Get all documents where the timestamp exceeds is not within the past month
const staleTokensResult = await admin.firestore().collection('fcmTokens')
.where("timestamp", "<", Date.now() - EXPIRATION_TIME)
.get();
// Delete devices with stale tokens
staleTokensResult.forEach(function(doc) { doc.ref.delete(); });
});
ביטול ההרשמה של טוקנים לא פעילים לנושאים
אם אתם משתמשים בנושאים, יכול להיות שתרצו גם לבטל את הרישום של טוקנים לא פעילים מהנושאים שהם רשומים אליהם. התהליך הזה כולל שני שלבים:
- האפליקציה צריכה להירשם מחדש לנושאים פעם בחודש, ובכל פעם שטוקן ההרשמה משתנה. כך נוצר פתרון לתיקון עצמי, שבו המינויים מופיעים מחדש באופן אוטומטי כשהאפליקציה הופכת שוב לפעילה.
- אם מופעלת באפליקציה תקופת חוסר פעילות של חודש (או תקופת חוסר פעילות שהגדרתם), צריך לבטל את ההרשמה של מופע האפליקציה לנושאים באמצעות Firebase Admin SDK כדי למחוק את המיפוי של הטוקן לנושא מהקצה העורפי של FCM.
היתרון בשני השלבים האלה הוא שההפצה תתבצע מהר יותר, כי יש פחות טוקנים לא פעילים להפצה, והמופעים הלא פעילים של האפליקציה יירשמו מחדש באופן אוטומטי ברגע שהם יהיו פעילים שוב.
מדידת הצלחת ההעברה
כדי לקבל תמונה מדויקת ככל האפשר של מסירת ההודעות, מומלץ לשלוח הודעות רק למופעים של אפליקציות שנמצאים בשימוש פעיל. זה חשוב במיוחד אם אתם שולחים הודעות באופן קבוע לנושאים עם מספר גדול של מנויים. אם חלק מהמנויים האלה לא פעילים, ההשפעה על נתוני המסירה שלכם יכולה להיות משמעותית לאורך זמן.
לפני שמטרגטים הודעות לטוקן, כדאי לשקול את הנקודות הבאות:
- האם נתונים מ-Google Analytics, נתונים שמתועדים ב-BigQuery או אותות מעקב אחרים מציינים שהאסימון פעיל?
- האם ניסיונות המסירה הקודמים נכשלו באופן עקבי במשך תקופה מסוימת?
- האם אסימון הרישום עודכן בשרתים שלך בחודש האחרון?
- במכשירי Android, האם FCM Data API
מדווח על אחוז גבוה של הודעות שלא נמסרו בגלל
droppedDeviceInactive?
מידע נוסף על מסירה זמין במאמר הסבר על מסירת הודעות.