שיטות מומלצות לניהול אסימוני רישום של FCM

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

טוקני רישום לא תקפים ותוקפם פג

אסימוני רישום לא תקפים הם אסימונים שמשויכים למכשירים לא פעילים שלא חוברו ל-FCM במשך יותר מחודש. עם הזמן, פוחת הסיכוי שהמכשיר יתחבר שוב אל FCM. סביר להניח שהודעות שנשלחות ואוספי נושאים של אסימונים לא תקינים לא יישלחו אף פעם.

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

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

שיטות מומלצות בסיסיות

יש כמה שיטות בסיסיות שכדאי לפעול לפיהן בכל אפליקציה שמשתמשת בממשקי API של FCM כדי ליצור בקשות שליחה באופן פרוגרמטי. השיטות המומלצות העיקריות הן:

  • אחזור אסימוני רישום מ-FCM ושמירתם בשרת אחד התפקידים החשובים של השרת הוא לעקוב אחרי האסימונים של כל לקוח ולשמור רשימה מעודכנת של האסימונים הפעילים. מומלץ מאוד להטמיע חותמת זמן של אסימון בקוד ובשרתים, ולעדכן את חותמת הזמן במרווחי זמן קבועים.
  • שמירה על עדכניות האסימונים והסרה של אסימונים לא תקינים בנוסף להסרת אסימונים שכבר לא נחשבים כחוקיים על ידי FCM, מומלץ לעקוב אחרי סימנים אחרים לכך שהאסימונים הפכו למיושנים ולהסיר אותם באופן יזום. המדריך הזה מתואר בחלק מהאפשרויות העומדות לרשותכם לצורך כך.

אחזור ואחסון של אסימוני רישום

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

  • 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(); });
});

ביטול ההרשמה לאסימונים לא פעילים לנושאים

אם אתם משתמשים בנושאים, כדאי גם לבטל את הרישום של אסימונים לא פעילים מהנושאים שאליהם הם מנויים. יש שני שלבים:

  1. האפליקציה שלכם צריכה להירשם מחדש לנושאים פעם בחודש ובכל פעם שאסימון הרישום משתנה. כך נוצר פתרון לתיקון עצמי, שבו המינויים מופיעים מחדש באופן אוטומטי כשאפליקציה הופכת שוב לפעילה.
  2. אם מכונה של אפליקציה לא פעילה במשך חודש (או בחלון הזמן שלכם לסטלות), עליכם לבטל את ההרשמה שלה לנושאים באמצעות Firebase Admin SDK כדי למחוק את המיפוי של האסימון לנושאים בקצה העורפי של FCM.

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

מדידת ההצלחה של הצגת המודעות

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

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

  • האם מערכת Google Analytics, נתונים שתועדו ב-BigQuery או אותות מעקב אחרים מצביעים על כך שהאסימון פעיל?
  • האם ניסיונות קודמים למשלוח נכשלו באופן עקבי לאורך זמן?
  • האם אסימון הרישום עודכן בשרתים שלך בחודש האחרון?
  • במכשירי Android, האם FCM Data API מדווח על אחוז גבוה של כשלים בהעברת הודעות בגלל droppedDeviceInactive?

מידע נוסף על העברה זמין במאמר הסבר על העברת הודעות.