אם אתם משתמשים ב-FCM APIs כדי ליצור בקשות שליחה באופן פרוגרמטי, יכול להיות שבמהלך הזמן תגלו שאתם מבזבזים משאבים בשליחת הודעות למכשירים לא פעילים עם טוקנים ישנים של רישום. המצב הזה יכול להשפיע על נתוני מסירת ההודעות שמוצגים במסוף Firebase או על הנתונים שמיוצאים ל-BigQuery, ולהופיע כירידה דרמטית (אבל לא באמת תקפה) בשיעורי המסירה. במדריך הזה נסביר על כמה אמצעים שאפשר לנקוט כדי לוודא שהודעות יפנו לקהל היעד הרלוונטי ושהדיווח על מסירת ההודעות יהיה מדויק.
טוקנים של הרשמה שפג תוקפם או שהם לא עדכניים
טוקנים לא עדכניים של רישום הם טוקנים שמשויכים למכשירים לא פעילים שלא התחברו ל-FCM במשך יותר מחודש. ככל שעובר הזמן, הסיכוי שהמכשיר יתחבר שוב אל FCM הולך ופוחת. סביר להניח שהודעות שנשלחות ונושאים שמופצים באמצעות טוקנים לא פעילים כאלה לא יגיעו ליעד.
יש כמה סיבות לכך שטוקן יכול להיות לא עדכני. לדוגמה, יכול להיות שהמכשיר שאליו משויך האסימון אבד, נהרס או אוחסן ונשכח.
כשמגיע טוקן לא פעיל ל-270 ימים של חוסר פעילות, מערכת FCM תסווג אותו כטוקן שתוקפו פג. אחרי שתוקף הטוקן פג, FCM מסמן אותו כלא תקף ודוחה שליחות אליו. עם זאת, FCM מנפיק טוקן חדש למופע של האפליקציה במקרה הנדיר שהמכשיר מתחבר שוב והאפליקציה נפתחת.
שיטות מומלצות בסיסיות
יש כמה שיטות בסיסיות שחשוב לפעול לפיהן בכל אפליקציה שמשתמשת בממשקי 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 (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 יחזיר תגובה של טוקן לא חוקי רק אם תוקף הטוקן פג אחרי 270 ימים או אם לקוח ביטל את ההרשמה שלו באופן מפורש. אם אתם רוצים לעקוב אחרי נתוני הטריות בצורה מדויקת יותר בהתאם להגדרות שלכם, אתם יכולים להסיר באופן יזום את טוקני הרישום שהתיישנו.
עדכון אסימונים באופן קבוע
מומלץ לאחזר ולעדכן מעת לעת את כל אסימוני הרישום בשרת. כדי לעשות את זה, צריך:
- מוסיפים לאפליקציית הלקוח לוגיקה לאחזור הטוקן הנוכחי באמצעות קריאה מתאימה ל-API (למשל,
token(completion):
לפלטפורמות של Apple אוgetToken()
ל-Android), ואז שולחים את הטוקן הנוכחי לשרת האפליקציה לאחסון (עם חותמת זמן). יכול להיות שמדובר בעבודה חודשית שהוגדרה לכלול את כל הלקוחות או האסימונים. - מוסיפים לוגיקה של שרת כדי לעדכן את חותמת הזמן של האסימון במרווחי זמן קבועים, ללא קשר לשאלה אם האסימון השתנה או לא.
דוגמה ללוגיקה של Android לעדכון טוקנים באמצעות WorkManager מופיעה במאמר ניהול טוקנים של העברת הודעות בענן בבלוג של 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
?
מידע נוסף על מסירה זמין במאמר הסבר על מסירת הודעות.