שילוב Android SDK
Android SDK נייטיבי למעקב אירועים, ניהול סשנים, שליחת התראות פוש דרך FCM והצגת הודעות בתוך האפליקציה.
יכולות
- קל משקל - טביעת רגל מינימלית
- מהיר - מותאם לביצועים
- תמיכה במצב לא מקוון - תור אירועים מבוסס SQLite
- ניסיונות חוזרים - השהיה מעריכית בין ניסיונות לאחר כישלון
- שליחה באצוות - שליחה יעילה באצוות (50 אירועים / 5 שניות)
- In-App Messaging - הודעות Native ו־HTML, 5 סוגים, עם הגבלת תדירות
- Push Notifications - Firebase Cloud Messaging (FCM)
- גישה ממוקדת פרטיות - תואם GDPR ומכבד הסכמת משתמש
- Android 6.0+ - תמיכה ב־API 23+
דרישות
- Android 6.0 (API level 23) ומעלה - הרף לאחסון מוצפן במנוחה (
EncryptedSharedPreferences), וגם המינימום ש-Firebase Cloud Messaging דורש - Kotlin 1.9.20 ומעלה
- Java 17 לבנייה עם Android Gradle Plugin 8. המחלקות של ה-SDK הן bytecode של Java 11, כך שגם בנייה עם JDK 11 (Unity 2022.3) מתקמפלת מולן
- Gradle 8.0 ומעלה
הגדרת SDK מקומית
לבנייה מקומית, הגדירו את נתיב ה־Android SDK בקובץ local.properties:
sdk.dir=/Users/your-user/Library/Android/sdk
לחלופין, ייצאו ANDROID_HOME/ANDROID_SDK_ROOT לפני הרצת Gradle.
התקנה
Gradle (מומלץ)
הוסיפו ל־build.gradle.kts:
ה־SDK מגיע בשני artifacts. הוסיפו אחד מהם - artifact ה־UI מכיל את הבסיס, כך שלעולם אין צורך להצהיר על שניהם:
dependencies {
// הכול, כולל הצגת הודעות In-App. התחילו כאן.
implementation("io.joryio:joryio-android-ui:1.3.3")
}
dependencies {
// מעקב, זהות ופוש בלבד - ללא הצגת In-App וללא WebView מקושר. בחרו בזה אם
// האפליקציה אינה משתמשת בהודעות In-App, או אם אתם מציגים אותן בעצמכם.
implementation("io.joryio:joryio-android:1.3.3")
}
או עם Groovy (build.gradle):
dependencies {
implementation 'io.joryio:joryio-android-ui:1.3.3'
}
joryio-android-ui תלוי ב־joryio-android, כך ששורה אחת מביאה את שניהם והם
לעולם לא ייצאו מסנכרון - אתם מציינים רק גרסה אחת.
קחו את artifact הבסיס לבדו כשלאפליקציה אין שימוש בהודעות In-App, או כשסקירת אבטחה מתנגדת לכך ש־WebView יהיה בר־קישור בכלל. הודעות In-App פשוט לא יוצגו; כל השאר עובד ללא שינוי.
התחלה מהירה
1. אתחול ה־SDK
במחלקת Application:
import io.joryio.sdk.Joryio
import io.joryio.sdk.JoryioConfig
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Initialize with your SDK key
Joryio.initialize(
context = this,
sdkKey = "jry_sdk_android_YOUR_SDK_KEY",
apiHost = "https://api-eu1.joryio.com/api"
)
}
}
מצאו את מפתח ה־SDK בלוח הבקרה של Joryio תחת Settings → Apps → [האפליקציה שלכם] → SDK Keys.
2. מעקב אירועים
// Basic event
Joryio.track("Button Tapped")
// Event with properties
Joryio.track("Product Viewed", mapOf(
"product_id" to "abc123",
"product_name" to "Wireless Headphones",
"price" to 99.99,
"category" to "Electronics"
))
// Screen view
Joryio.trackScreen("ProductDetail", mapOf(
"product_id" to "abc123"
))
3. זיהוי משתמשים
שלחו תמיד מספרי טלפון בפורמט בינלאומי מלא (E.164): +, קידומת המדינה ואז המספר - למשל +972501234567. מספר בלי + נקרא לפי מדינת ברירת המחדל למספרי טלפון של סביבת העבודה; אם לא הוגדרה, או שאפשר לקרוא את הספרות בשתי דרכים, הוא נשמר כלא מאומת ולא משמש להודעות. איך מטופלים מספרי טלפון
// Identify a user
Joryio.identify("user-123")
Joryio.getInstance().setAttributes(mapOf(
"name" to "John Doe",
"plan" to "premium"
))
// Set attributes later
Joryio.setAttribute("last_purchase", Date())
Joryio.incrementAttribute("lifetime_value", 99.99)
// Attach another identifier of yours (label: lowercase letters, digits, _).
// Works before identify() too - it follows the visitor through login.
Joryio.addAlias("loyalty_id", "L-12345")
// On logout: forgets the user AND starts a new anonymous ID
Joryio.reset()
identify הוא הדרך שבה מבקר אנונימי הופך למשתמש מזוהה; אין קריאת alias(userId) נפרדת. reset() גם מנקה כתיבות מאפיינים שעוד לא נשלחו וכל הודעת In-App שממתינה להצגה, רושם מחדש את טוקן הפוש תחת המזהה האנונימי החדש ומסנכרן שוב הודעות In-App, כך שהאדם הבא במכשיר לעולם לא נרשם כקודמו.
קריאות ל-Joryio שנעשות לפני שהאתחול מסתיים נשמרות בתור ומורצות לפי הסדר ברגע שהוא מסתיים, גם עם initialize וגם עם initializeAsync. אף קריאה ציבורית לא זורקת חריגה לתוך הקוד שלכם: ארגומנט חסר (null מ-Java) נרשם ביומן ומתעלמים ממנו. החריגה היחידה היא Joryio.getInstance() לפני initialize() - ראו למטה.
נתוני סשן אוטומטיים
Android SDK מעשיר אירועי Session Start בנתוני מכשיר וסביבה:
$device_id$platform(android)$manufacturer/$model$os_name/$os_version/$os_sdk_int$app_version/$build_number$package_name$screen_width/$screen_height$locale$language/$languages$timezonecountry(ISO-3166-1 alpha-2, נגזר מה־IP בתחילת סשן)
התראות פוש
בקשו את ההרשאה כשלמשתמש יש סיבה להסכים. בקשה בשניות הראשונות של הסשן היא זו שנדחית הכי הרבה, וסירוב הרבה יותר קשה לשינוי מאשר עיכוב - בקשת המערכת היא חד-פעמית, ולכן "לא" שם הוא למעשה קבוע, בעוד שבקשה מאוחרת יותר לא עולה לכם דבר.
ה-SDK לעולם לא מציג את הבקשה במקומכם. אם החלטתם שאתם כן רוצים אותה כבר בהפעלה,
הגדירו requestPushPermissionAtLaunch - היא כבויה כברירת מחדל, לא חסרה.
הפעילו התראות פוש עם Firebase Cloud Messaging (FCM).
1. הוספת Firebase לפרויקט
עקבו אחרי מדריך ההתקנה של Firebase כדי להוסיף Firebase לפרויקט Android שלכם.
2. הוספת Service ל־AndroidManifest.xml
<service
android:name="io.joryio.sdk.push.JoryioFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
3. הרשמה לקבלת פוש
import com.google.firebase.messaging.FirebaseMessaging
// Get FCM token and register
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (task.isSuccessful) {
val token = task.result
Joryio.getInstance().registerPushToken(token) { result ->
if (!result.success && result.isRetryable) {
// תקלה זמנית (אופליין, DNS, 5xx): רשמו שוב כשהרשת חוזרת
}
if (result.isUnauthorized) {
// מפתח SDK שגוי - ניסיון חוזר לא יעזור
}
}
}
}
// Check if push is enabled
val isEnabled = Joryio.getInstance().isPushEnabled()
// Unregister when needed
Joryio.getInstance().unregisterPush()
העבירו onResult כדי לדעת מה קרה: isRetryable פירושו תקלה זמנית (אופליין, DNS, 5xx) שכדאי לרשום שוב אחריה; isUnauthorized פירושו שמפתח ה-SDK שגוי. בלעדיו הקריאה היא שגר-ושכח. הרשמה נחשבת רק כשהשרת מאשר אותה; תשובה של success: false מדווחת ככישלון ומנוסה שוב בחזרה הבאה לחזית. אחרי unregisterPush(), קריאה ל-registerPushToken() רושמת את המכשיר מחדש. אל תעבירו ל-registerPushToken() את קריאות הטוקן האוטומטיות של Firebase (onNewToken() בשירות ההודעות שלכם, הטוקן שנשלף בכל הפעלה) כשהמשתמש כיבה את הפוש: זו הקריאה שמדליקה את הפוש מחדש. שירות ההודעות של ה-SDK מטפל בטוקנים חדשים בעצמו ולעולם לא מבטל את unregisterPush(), ו-Joryio.unregisterPush() שנקרא לפני initialize() גובר על הטוקן ש-Firebase מוסר בהפעלה.
requestPushPermission(activity) עובד עם כל Activity. כשה-Activity שלכם הוא android.app.Activity פשוט (כמו ב-Unity), ה-SDK מציג את דיאלוג המערכת מתוך activity בלתי נראה משלו וקורא לכם בחזרה כשהמשתמש עונה. PushPermissionStatus.publicName מחזיר את הסטטוס בשם שבו משתמש כל SDK של Joryio: granted, denied, notDetermined או unavailable.
ערוץ ההתראות של ה-SDK נקרא "Notifications" בהגדרות המערכת. הגדירו notificationChannelName ב-JoryioConfig כדי להציג שם משלכם, מתורגם.
4. הגדרת FCM בלוח הבקרה
כדי לשלוח התראות פוש, הזינו את אישורי Firebase באפליקציית ה־Android ב־Joryio:
- עברו אל הגדרות → אפליקציות ולחצו על סמל התראות Push (הפעמון) בשורה של אפליקציית ה־Android
- לחצו על Upload service account JSON ובחרו את מפתח חשבון השירות של Firebase (בקונסולת Firebase: Project settings → Service accounts → Generate New Private Key)
- לחצו על שמור הגדרות
להוראות מפורטות ליצירת המפתח, ראו קבלת אישורי Firebase (FCM).
הודעות בתוך האפליקציה (In-App Messaging)
ה־SDK מציג עבורכם הודעות In-App. לאחר האתחול, קמפיינים מתאימים מופיעים מעצמם, וחשיפות, קליקים וסגירות נמדדים אוטומטית - אין מה לחבר.
אסימוני שליחה (delivery tokens)
כאשר השרת מגיש קמפיין מתאים, הוא מנפיק עבורו גם אסימון שליחה חתום וקצר-מועד. ה-SDK מחזיר את האסימון כשהוא מדווח על הצגה, קליק או סגירה, והשרת מאמת את החתימה לפני שהוא רושם משהו.
אינך צריך לעשות דבר - ה-SDK מטפל בזה עבורך. התיעוד כאן קיים כי ניתן להגדיר את השרת לדרוש את האסימון. כברירת מחדל דיווח ללא אסימון עדיין מתקבל - השרת רושם אותו ביומן כלקוח מגרסה שקדמה לאסימונים - ורק כאשר השרת רץ עם IN_APP_REQUIRE_DELIVERY_TOKEN=true אותו דיווח נדחה:
POST /v1/in-app/track (no deliveryToken)
{ "success": false, "error": "A delivery token is required" }
אסימון שנשלח אך אינו עובר אימות - מזויף, שונה או שפג תוקפו - נדחה תמיד, ללא קשר להגדרה.
האסימון הוא מה שהופך דיווח הצגה לאמין: בלעדיו, כל מי שמחזיק במפתח ה-SDK - שנשלח בתוך כל אפליקציה ובכל עמוד - היה יכול לדווח על הצגות וקליקים לקמפיין שמעולם לא הוצג, והדוחות שלך היו סופרים אותם.
אם הצגות in-app מפסיקות להירשם מול שרת שאוכף אסימונים, ודא שהאפליקציה נבנתה מול גרסה עדכנית של ה-SDK: בנייה שנעשתה לפני שאסימוני השליחה נוספו אינה שולחת אסימון.
שני סוגי תוכן
כל קמפיין מגיע באחת משתי צורות תוכן, וה־SDK מציג כל אחת מהן אחרת:
| תוכן | מה זה | איך זה מוצג |
|---|---|---|
| Native | נתונים מובנים - כותרת, טקסט, תמונה, כפתורים | View־ים אמיתיים של Android, עם ערכת הנושא, הגופנים, המצב הכהה ו־TalkBack של האפליקציה. ללא WebView. |
| HTML | קוד HTML ו־CSS שנכתב על ידי המחבר. נשמר כפי שנכתב (רק <base> ו־<meta http-equiv="refresh"> מוסרים) | WebView בתוך ההודעה, תחת Content-Security-Policy שבו רק סקריפט הגשר של ה־SDK רץ. |
הפעלת הודעות HTML
הודעות HTML כבויות כברירת מחדל. סקריפטים שנכתבו בהודעה לא רצים: כל הודעה מקבלת nonce חדש ב־Content-Security-Policy, כך שרק סקריפט הגשר של ה־SDK רץ - בלוקי <script>, מטפלי on* והטמעות של המחבר נשארים מושבתים, והגשר מטפל בכפתורים, במאפייני data-joryio-action ובטפסי data-jry-form. זה עדיין WebView בתוך האפליקציה שלכם, ולכן ההפעלה היא החלטה של צוות האפליקציה - לא משהו שמפעילים ממערכת השיווק:
val config = JoryioConfig(
allowHtmlJsInAppMessages = true // ברירת מחדל: false
)
Joryio.initialize(
context = this,
sdkKey = "jry_sdk_android_YOUR_KEY",
apiHost = "https://api-eu1.joryio.com/api",
config = config
)
השארת ההגדרה כבויה אינה מבטלת הודעות In-App. הודעות Native ימשיכו להופיע, כי הן נתונים שהאפליקציה מציירת עם ה־View־ים שלה - ללא מפרש כלשהו. קמפייני HTML מדולגים ונרשמים ללוג, כך שאפליקציה שלא הפעילה את ההגדרה לא מציגה דבר במקום להציג הודעה שבורה.
אם מדיניות האבטחה שלכם אוסרת הרצת HTML שנכתב חיצונית בתוך התהליך, השאירו את ההגדרה כבויה וכתבו את הקמפיינים כהודעות Native.
השתלטות על הרינדור
הגדירו callback כדי לצייר ממשק משלכם. הוא דורס את הרינדור של ה־SDK, כך שלא תקבלו שני עותקים של ההודעה:
Joryio.getInstance().setInAppMessageCallback { campaign ->
showInAppMessage(campaign) // הממשק שלכם
}
// דווחו מה קרה - ה־SDK מודד אוטומטית רק הודעות שהוא עצמו מציג
Joryio.getInstance().trackInAppImpression(campaignId, "displayed")
Joryio.getInstance().trackInAppImpression(campaignId, "clicked", "cta") // איזה כפתור
Joryio.getInstance().trackInAppImpression(campaignId, "dismissed")
הפעולה היא אחת מ-displayed, clicked, dismissed או converted; כל ערך אחר נרשם ביומן ולא נשלח. רק displayed נספר במגבלת התדירות של הקמפיין - קליק או סגירה לא מנצלים הצגה נוספת. הארגומנט השלישי, elementId, מציין את הכפתור או הקישור ונשלח בנפרד מהפעולה.
החל מגרסה 1.3.2. אם אתם מגדירים את ה-callback אחרי initialize(), הסנכרון הראשון יכול להסתיים לפניו וה-SDK מצייר את ההודעה הזו בעצמו. כדי למנוע זאת, העבירו JoryioConfig(holdInAppMessagesForHandler = true): הודעה שמוכנה לפני שה-callback הוגדר מוחזקת - לא מצוירת, לא מדווחת, ולא נספרת במגבלת התדירות - ומועברת ל-callback ברגע שתגדירו אותו. קריאה ל-clearInAppMessageCallback() במקום זאת מסיימת את ההחזקה, וה-SDK מצייר את ההודעה המוחזקת. ההחזקה חלה רק עד ההגדרה או הניקוי הראשונים.
רינדור ההודעות בעצמכם
ה-SDK מצייר עבורכם הודעות Native, אבל אפשר לקחת שליטה מלאה - המקבילה ל-custom view factory:
Joryio.getInstance().setInAppMessageCallback { campaign ->
// ציירו כרצונכם
}
הגדרת callback דורסת את הרנדרר המובנה במקום לרוץ לצידו, כך שההודעה מוצגת פעם אחת ולא פעמיים.
השדה campaign.content כבר מוכן - ה-Liquid מרונדר בצד השרת, ושדות Native מגיעים
כטקסט רגיל (אל תעבירו אותם ל-WebView; הם אינם עוברים HTML-escape, בדיוק מפני
שהם מיועדים ל-TextView). דווחו על מה שהצגתם באמצעות
trackInAppImpression(campaignId, "displayed" | "clicked" | "dismissed" | "converted")
כדי שהאנליטיקה עדיין תהיה תואמת.
סוגי הודעות
ה־SDK תומך ב־5 סוגי הודעות:
- Modal - במרכז המסך עם רקע מוצל
- Banner - בראש המסך
- Slide-Up - התראה קטנה מלמטה
- Full-Screen - הודעה שתופסת את כל המסך
- Custom - האפליקציה קובעת את המיקום
מתי הודעה נחשבת כמוצגת
הודעה נחשבת כמוצגת - כלומר in_app.displayed מדווח ומגבלת התדירות של הקמפיין נצרכת - רק כשהאדם יכול היה לראות אותה. משך הזמן שעליה להיות על המסך הוא IN_APP_DISPLAY_DWELL_MS (1000 מילישניות), אותו קבוע בכל ה-SDK של Joryio.
- ה-SDK מציג הודעה רק ב-Activity הפעיל (resumed), ורק אם הוא לא נסגר, לא הושמד ולא נבנה מחדש (סיבוב מסך). אחרת ההודעה נשמרת למסך הבא.
- ההודעה נחשבת כמוצגת אחרי שהייתה על המסך שנייה אחת כשה-Activity נשאר פעיל. אם ה-Activity מושהה לפני כן, השנייה מתחילה מחדש כשהוא חוזר.
- אם המסך נסגר לפני כן - הפניה אחרי התחברות, התנתקות - שום דבר לא מדווח והמגבלה לא נצרכת; ההודעה מוצעת במסך הבא.
- הקשה או סגירה לפני כן נחשבות כצפייה: קודם מדווח
displayed, ואחריו הלחיצה או הסגירה. איפוס, החלפת משתמש או ביטול הסכמה לפני כן לא מדווחים דבר. - אם אתם מציגים את ההודעות בעצמכם: דווחו
displayedלפי אותו כלל - אחרי שההודעה הייתה גלויהIN_APP_DISPLAY_DWELL_MS, או ממש לפני לחיצה או סגירה שמגיעות מוקדם יותר.InAppMessagePresenterמותאם שולחInAppMessagePresenter.EVENT_HIDDENכשהוא מסיר הודעה בלי שהאדם פעל עליה.
שליחת מאפיינים
setAttributes עמיד. כתיבה נשמרת בתור עד שהשרת מאשר אותה, כך שמאפיין שנקבע
כשהמכשיר לא מחובר נשלח כשהחיבור חוזר במקום להימחק.
- ניסיון חוזר בקריאת
setAttributesהבאה, בחזרה לחזית, ולפני סנכרון in-app. - מקובץ - רצף קריאות הופך לבקשה אחת (חלון של 800 מילישניות). כתיבה בודדת עדיין נשלחת מיד.
- נשמר באחסון המוצפן של הפלטפורמה (iOS Keychain, Android EncryptedSharedPreferences), כך שכתיבה שורדת סגירה של התהליך. נמחק ברגע שהשרת מאשר, ומנוקה על ידי
optOut()ו-wipeData().
מאפיינים משמשים גם למיקוד in-app. פרופיל השרת הוא המקור הקובע: רק כתיבות שהשרת עוד לא אישר יכולות לגבור עליו - וזה מה שמונע משני מכשירים של אותו איש קשר לחלוק על מי הוא.
אפשרויות תצורה
התאימו את התנהגות ה־SDK עם JoryioConfig:
val config = JoryioConfig(
// Initial user ID (optional)
userId = "user-123",
anonymousId = null, // Your own anonymous id; generated if null
// Event batching
batchSize = 50, // Events per batch (at most 500)
flushInterval = 5000, // Flush interval in ms (5s); never below 1000
sendImmediately = false, // Flush after every track, no batching
maxQueueSize = 1000, // Most events kept on the device; the oldest go first
// Session management
sessionTimeout = 1800000, // Session timeout in ms (30 min), background included
trackSessionStart = true, // Auto-track session start
// Network retry
maxRetries = 3, // Immediate retries of one request before backing off
retryBackoffMs = 1000, // Base backoff between retries (ms)
requestTimeout = 10000, // Per-request HTTP timeout (ms)
// SDK authentication (optional, JWT)
enableSdkAuthentication = false,
sdkAuthenticationToken = null,
// Privacy controls
optOut = false, // Start opted out (remembered, like optOut())
trackingConsent = null, // null = not set; see Tracking Consent below
// Push: ask for notification permission at the first foreground
requestPushPermissionAtLaunch = false,
// Push: the channel name users see in system settings (Android 8+)
notificationChannelName = "Notifications",
// Debugging
enableDebug = false, // Enable debug logging
logLevel = null // null (default): follows enableDebug. Set to pin a level.
)
Joryio.initialize(
context = this,
sdkKey = "jry_sdk_android_YOUR_KEY",
apiHost = "https://api-eu1.joryio.com/api",
config = config
)
הסכמת מעקב
enum class TrackingConsent {
GRANTED, // Full tracking allowed
PENDING, // Waiting for user decision
DENIED // User denied tracking
}
הסכמה והסרת הסכמה (opt-out) הן שני מתגים נפרדים, זהים בכל SDK של Joryio:
| מתג | מי קובע אותו | קריאות |
|---|---|---|
| הסכמת מעקב | באנר ההסכמה שלכם | updateConsent(...), trackingConsent בתצורה |
| הסרת הסכמה (opt-out) | הבחירה של האדם עצמו | optOut() / optIn(), optOut בתצורה |
ה-SDK אוסף רק כשההסכמה היא GRANTED וגם האדם לא ביטל את הסכמתו - כל "לא" גובר. הסכמה לעולם לא מבטלת opt-out (רק optIn() עושה זאת), ו-optOut() לעולם לא משנה את ההסכמה השמורה.
Joryio.updateConsent(TrackingConsent.GRANTED) // כל מה שנשמר נשלח, לפי הסדר
Joryio.updateConsent(TrackingConsent.DENIED) // כל מה שנשמר נמחק
Joryio.updateConsent(TrackingConsent.PENDING) // הכול נשמר במכשיר
PENDINGשומר אירועים ומאפיינים במכשיר (עדmaxQueueSize) ולא שולח דבר - אין הודעות In-App, אין הרשמה לפוש - עד ש-GRANTEDמשחרר אותם לפי הסדר, או ש-DENIEDמוחק אותם. הוא חל על ההפעלה הנוכחית בלבד ולעולם לא דורסDENIEDשמור. גם לחיצה על התראה מוחזקת ומדווחת עםGRANTED.DENIEDלא אוסף דבר ומוחק את מה שנשמר. הוא נזכר במכשיר: נשאר בתוקף גם אחרי הפעלה מחדש, עדupdateConsent(TrackingConsent.GRANTED)אוtrackingConsent = TrackingConsent.GRANTEDמפורש בתצורה. הוא אינו מבצע opt-out לאדם -isUserOptedOut()נשארfalse.- לא נקבע (
trackingConsent = null, ברירת המחדל):DENIEDשמור נשאר בתוקף; אחרת ה-SDK מתנהג כמוGRANTED, וזה הנכון לאפליקציה ללא באנר הסכמה.
קראו את הערך הנוכחי עם getTrackingConsent().
רמות לוג
enum class LogLevel {
VERBOSE, // All logs
DEBUG, // Debug and above
INFO, // Info and above
WARN, // Warnings and errors
ERROR // Errors only
}
logLevel הוא null כברירת מחדל, כלומר "עקוב אחרי enableDebug": DEBUG כשהדיבאג דולק, ERROR כשהוא כבוי. הגדירו אותו במפורש רק כדי לקבע רמה - LogLevel.ERROR מפורש עם enableDebug = true משאיר את ה-SDK שקט.
יכולות מתקדמות
ניהול סשנים
סשנים עוקבים אוטומטית אחרי מעורבות המשתמש:
// Sessions are managed automatically with 30-minute timeout
// Get current session ID
val sessionId = Joryio.getInstance().getSessionId()
// Sessions refresh on user activity
זמן ברקע נספר לטובת ה-timeout: אפליקציה שחוזרת לחזית אחרי יותר מ-sessionTimeout פותחת סשן חדש ושולחת Session Start חדש.
מאפייני משתמש
// Set multiple attributes
Joryio.getInstance().setAttributes(mapOf(
"age" to 28,
"city" to "San Francisco",
"premium" to true
))
// Set single attribute
Joryio.setAttribute("language", "en")
// Increment numeric attribute
Joryio.incrementAttribute("page_views", 1)
Joryio.incrementAttribute("total_spent", 29.99)
// Remove attribute
Joryio.getInstance().unsetAttribute("temporary_flag")
בקרות פרטיות
// הפסקת איסוף. נשמר במכשיר - שורד הפעלה מחדש של האפליקציה.
Joryio.optOut()
// חזרה להסכמה - הקריאה היחידה שמבטלת opt-out
Joryio.optIn()
// מתג ה-opt-out בלבד; את ההסכמה קוראים עם getTrackingConsent()
if (Joryio.isUserOptedOut()) {
Log.i("MyApp", "User has opted out")
}
// מחיקת כל מה שה-SDK שמר במכשיר הזה.
// נפרד מ-optOut(): "להפסיק לאסוף" ו"למחוק את מה שכבר נאסף" הן
// בקשות שונות. זו הפעולה שנדרשת לבקשת מחיקה. היא אינה מבטלת
// הסכמה - יש לקרוא גם ל-optOut() אם זו הכוונה.
Joryio.wipeData()
// קבלת פרטי הזהות
val (userId, anonymousId) = Joryio.getInstance().getIdentity()
println("User: ${userId ?: "anonymous"}, Anonymous ID: $anonymousId")
מה בדיוק עושה optOut():
| מפסיק איסוף | כן - כל כתיבה הופכת ללא-פעולה: אירועים, identify, מאפיינים (כולל הגדלות ומחיקות), כינויים (aliases), הרשמות למנוי, הרשמה לפוש, אישורי מסירה ומעקב קליקים, דיווחי In-App. ביטולי מנוי וביטול רישום לפוש עדיין נשלחים |
| שורד הפעלה מחדש | כן - הדגל נשמר במכשיר ונקרא לפני כל איסוף |
| מוחק את מה שכבר בתור | כן - אירועים בתור וכתיבות מאפיינים שלא אושרו נמחקים, לא נשלחים מאוחר יותר. ביטול מנוי שעוד לא נמסר נשמר ונשלח |
| מעדכן את השרת | כן - מאפיין הפרופיל tracking_opted_out, שנשלח כשהמתג משתנה (גם עבור opt-out שהוחלט באתחול או לפניו) ונשלח שוב עד שהשרת קיבל אותו, בלי קשר להסכמה עבור true (ה-false של optIn() ממתין להסכמה GRANTED) |
| מוחק מידע שמור | לא - יש להשתמש ב-wipeData() |
המאפיין tracking_opted_out הוא תיעוד, לא אכיפה: הוא נשמר בפרופיל כדי
שקמפיינים יוכלו להחריג לפיו. חסימה בצד השרת היא הגדרה נפרדת.
ריקון תור ידני
// Flush events immediately
Joryio.flush()
// Useful before app termination
override fun onDestroy() {
super.onDestroy()
Joryio.flush()
}
שיטות עבודה מומלצות
1. אתחול מוקדם
אתחלו במחלקת Application:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Joryio.initialize(
context = this,
sdkKey = "jry_sdk_android_YOUR_KEY",
apiHost = "https://api-eu1.joryio.com/api"
)
}
}
2. מעקב אחר מסכים
השתמשו במעקב מסכים לניווט:
override fun onResume() {
super.onResume()
Joryio.trackScreen(this::class.simpleName ?: "Unknown")
}
3. טיפול בהתנתקות
תמיד אפסו בעת יציאה:
fun logout() {
// Clear user session
clearUserSession()
// Reset SDK
Joryio.reset()
}
שליחה, הסכמה ומחזור חיי הנתונים
כתיבות נשמרות עד שהשרת מאשר אותן. identify, addAlias, incrementAttribute, unsetAttribute, setSubscription וקריאות קבוצות המנוי נשמרות בתיבת יציאה מוצפנת במכשיר ונשלחות לפי הסדר שבו נעשו. כישלון - אופליין, timeout, 5xx, 429, או 200 שעונה success: false - משאיר את הכתיבה ומנסה שוב עם המתנה הולכת וגדלה, גם אחרי הפעלה מחדש. Retry-After מכובד, בשניות או כתאריך, וגם flush() ממתין לו. כתיבה שהשרת לעולם לא יקבל - 4xx כמו 400, 409, 413 או 422, או 403 ממדיניות הזהות שלכם - נמחקת עם שורה ביומן, כדי שלא תחסום את אלה שאחריה. אופליין, timeout, 429 או 5xx לעולם לא נספרים נגד כתיבה. 401 או 200 עם success: false מנוסים שוב לכל היותר עשר פעמים לאותה כתיבה, ו-404 (עדיין אין פרופיל) לכל היותר חמש: שינוי מנוי שנענה ב-404 מנוסה שוב אחרי שהאירועים שבתור נשלחים, כי הם אלה שיוצרים את הפרופיל. ערכים ש-setAttributes שולח מקבלים אותן עשר תשובות. קריאות מאפיינים על אותו מפתח מוחלות לפי הסדר שבו ביצעתם אותן, לא משנה באיזו מ-setAttribute, unsetAttribute ו-incrementAttribute השתמשתם. ל-setAttributes יש תור עמיד משלו, המתואר למעלה; מאפיינים שהשרת דוחה כלא תקינים (400) נמחקים גם שם.
מגבלות אירועים. השרת דוחה אירוע שהמאפיינים שלו גדולים מ-50 KB, כוללים יותר מ-200 מפתחות או מקוננים יותר מ-5 רמות, או ששמו ריק או ארוך מ-255 תווים. track() בודק את אלה במכשיר, מודד גודל כפי שהשרת מודד (תו כמו < או & נספר פעם אחת), ומשליך אירוע כזה עם שורה ביומן, במקום להכניס לתור משהו שלעולם לא יימסר. שם שמורכב רק מרווחים הוא שם תקין. אירוע שהשרת עדיין דוחה באופן סופי נמחק, ולא מנוסה שוב.
ערך null מוחק מאפיין. setAttribute("plan", null) זהה ל-unsetAttribute("plan"), בכל SDK של Joryio.
סשנים. סשן מסתיים אחרי sessionTimeout ללא פעילות, בין אם האפליקציה הייתה ברקע ובין אם נשארה פתוחה על מסך אחד. האירוע הראשון אחרי זה פותח סשן חדש ושולח קודם את ה-Session Start שלו. גם reset() פותח סשן חדש. הפעלה בתוך sessionTimeout מהפעילות האחרונה ממשיכה את אותו סשן ולא שולחת Session Start חדש. סשן שהתחיל בזמן שהמעקב היה חסום (opt-out או הסכמה שנדחתה) לא ממשיך כך: ההפעלה הראשונה שמותר לה לעקוב פותחת סשן חדש עם ה-Session Start שלו.
הסכמה בהפעלה. optOut = true פועל כמו optOut(): הוא נזכר במכשיר, כך שהפעלה מאוחרת יותר נשארת ללא מעקב עד optIn(). גם trackingConsent = TrackingConsent.DENIED נזכר, כהסכמה (ראו הסכמת מעקב). בשני המקרים, כל מה שהפעלה קודמת השאירה בתור נמחק. Joryio.optOut(), Joryio.optIn(), Joryio.updateConsent() ו-Joryio.wipeData() שנקראים לפני initialize() מוחלים על ידי initialize() עצמו, לפני שמשהו נאסף או נשלח - ונשמרים במכשיר עד אז, כך שהחלטה שהתקבלה לפני שה-SDK הופעל שורדת סגירה של האפליקציה. לפני initialize(), Joryio.isUserOptedOut() ו-Joryio.getTrackingConsent() עונים לפי מה שנשמר במכשיר - opt-out או סירוב מהפעלה קודמת - ולא לפי ערך ברירת מחדל. Joryio.updateConsent(TrackingConsent.PENDING) לפני initialize() חל על אותה הפעלה בלבד ולעולם לא פותח מחדש סירוב, ו-Joryio.optIn() או Joryio.optOut() לפני initialize() גוברים על optOut בתצורה. כשלא נשמר דבר, Joryio.getTrackingConsent() עונה GRANTED, כמו ב-iOS.
אימות SDK. כש-enableSdkAuthentication פעיל, ה-handler שהוגדר עם setSdkAuthenticationErrorHandler נקרא בכל 401 של אימות, כולל כשעוד לא הוגדר טוקן (סיבה MISSING): ספקו טוקן עם setSdkAuthenticationToken. אירועים נשלחים באצוות של משתמש אחד בכל פעם; אירועים או כתיבות של משתמש שכבר התנתק, שהשרת דוחה מכל סיבת אימות שהיא, נמחקים, כך שלעולם לא יעכבו את אלה של המשתמש הבא. ה-handler נקרא רק עבור בקשות של המשתמש המחובר עכשיו, לעולם לא עבור שאריות של משתמש שהתנתק. reset() מוציא את הטוקן מהסשן; הוא נשאר עם מה שהמשתמש היוצא השאיר בתור. כל בקשה נושאת את הטוקן שה-sub שלו הוא המשתמש של אותה בקשה - לעולם לא טוקן שמציין מישהו אחר - ובקשה של מבקר אנונימי לא נושאת טוקן. טוקן שהוגדר למשתמש הבא לפני reset(), גם כשאף אחד לא מחובר, נשמר עבורו ומשמש שוב ב-identify() שלו; wipeData() מוחק אותו. אחרת, הגדירו את הטוקן של המשתמש הבא עם setSdkAuthenticationToken אחרי שהוא מתחבר. אירועים וכתיבות שהמשתמש היוצא כבר הכניס לתור עדיין נשלחים עם הטוקן שלו, כך שהתנתקות רגילה לא מאבדת דבר.
לפני האתחול. קריאות ל-Joryio שנעשות לפני ש-initialize() מסתיים נשמרות - עד 1000, הוותיקות נמחקות ראשונות - ומורצות לפי הסדר. אירוע, כתיבת מאפיין, alias או הרשמה שנעשים דרכן בזמן שהמשתמש ביטל את המעקב (opt-out) או שההסכמה נדחתה - בהחלטה שנעשתה לפני initialize() או שנשמרה בהפעלה קודמת - נמחקים מיד, כך ש-optIn() או מתן הסכמה מאוחרים יותר לעולם לא שולחים אותם; ביטולי מנוי, identify() ו-reset() נשמרים. optOut() או updateConsent(DENIED) לפני initialize() מוחקים גם את האירועים, כתיבות המאפיינים, ה-aliases וההרשמות שנכנסו לתור לפניהם, כמו אחרי האתחול, כך ש-optIn() או מתן הסכמה מאוחרים יותר לפני initialize() לעולם לא שולחים אותם. טוקן שנקבע עם Joryio.setSdkAuthenticationToken() לפני כל identify(), reset() או wipeData() מוחל על ידי initialize() עצמו, לפני הבקשה הראשונה שלו. הגדירו את הטוקן ואת ה-handler עם המתודות הסטטיות Joryio.setSdkAuthenticationToken() ו-Joryio.setSdkAuthenticationErrorHandler(), לא בתוך whenReady: רק הן נלקחות לפני הבקשה הראשונה ונשמרות דרך wipeData() שנעשה לפני initialize(), כך שביטולי המנוי של המשתמש שנמחק עדיין יוצאים עם הטוקן שלו. Joryio.getInstance() היא הקריאה היחידה שזורקת חריגה (IllegalStateException) לפני initialize(); במקום שבו ייתכן שהאתחול עוד לא הסתיים, השתמשו במתודות הסטטיות או ב-Joryio.whenReady { sdk -> ... }.
חשבון מושהה (402). 402 פירושו שקליטת הנתונים בחשבון שלכם מושהית. הוא אף פעם לא נספר במגבלת הניסיונות של כתיבה, והוא לא מעכב ביטולים: ביטול מנוי, יציאה מקבוצה והתיעוד tracking_opted_out = true שממתינים מאחורי כתיבה שנדחתה עדיין נשלחים, ושאר הכתיבות ממתינות ונשלחות שוב עם המתנה הולכת וגדלה.
התנתקות והחלפת חשבון. טוקן אימות ה-SDK של המשתמש היוצא נשמר באחסון המוצפן יחד עם האירועים והכתיבות שלו שממתינים בתור, כך שהם נמסרים איתו גם אם האפליקציה נסגרת לפני שנשלחו; הוא נמחק אחרי שנמסרו. identify() של משתמש אחר בלי reset() מטופל באותו אופן עבור המשתמש הקודם: המאפיינים שלו שעוד לא נשלחו נשלחים תחת המזהים שלו, והטוקן הולך עם מה שנשאר לו בתור - אלא אם כבר הגדרתם את הטוקן של המשתמש החדש, שה-SDK מזהה לפי ה-sub שלו. ביטול מנוי שממתין בתור מוחלף רק בשינוי חדש יותר של אותו משתמש.
identify() כשיש opt-out או כשההסכמה נדחתה. מזהה המשתמש נשמר במכשיר בלבד - שום דבר לא נשלח ואף פרופיל לא נכתב - כדי שביטול מנוי שנעשה בינתיים יישא אותו. כשמעקב מותר שוב (optIn(), updateConsent(TrackingConsent.GRANTED)), identify() רץ כרגיל. reset() ו-wipeData() שוכחים אותו. זה קורה גם בהפעלה מאוחרת יותר שהמצב השמור או התצורה שלה מתירים מעקב. identify() חדש מחליף את מזהה המשתמש המוחזק, ו-userId בתצורה גובר עליו. גם רשומת ה-opt-out וביטול רישום לפוש שנעשו בינתיים נושאים אותו, ותחת SDK Authentication דחייה שלהם מבקשת מה-handler שלכם את הטוקן של אותו משתמש.
מספרים ש-JSON לא יכול לשאת. NaN או Infinity בערך של מאפיין או במאפיין של אירוע או של מסחר אלקטרוני מושמט - רק המפתח הזה - עם שורת לוג, ושאר הקריאה ממשיכה. שורה null ברשימת items של מסחר אלקטרוני מדולגת.
קריאת הסשן לא משנה דבר. getSessionId() ו-getSessionInfo() לעולם לא מתחילים או מאריכים סשן. רק אירועים וחזרת האפליקציה לחזית עושים זאת, וסשן חדש תמיד שולח את ה-Session Start שלו.
מאגר מלא לפני האתחול. כשמאגר 1000 הקריאות מלא, נמחקת ראשונה הקריאה הוותיקה ביותר שאינה ביטול מנוי, יציאה מקבוצה או Joryio.unregisterPush().
מפתחות, אחסון ו-threads. מפתח SDK שהונפק לפלטפורמה אחרת (jry_sdk_ios_..., jry_sdk_web_..., חנות) נדחה עם שגיאה וה-SDK לא שולח דבר. שגיאת Keystore מנוסה שוב ולעולם לא מוחקת את האחסון המוצפן; רק אחסון שלא ניתן לפענח (שחזור מגיבוי) נוצר מחדש, והפעלה שרצה בזמן שה-Keystore לא היה זמין מתחילה מקובץ רגיל בלי הזהות השמורה; בהפעלה הבאה שפותחת את האחסון המוצפן, השינוי האחרון שלה בכל ערך גובר - כולל התנתקות, wipeData(), optIn() או מתן הסכמה שנעשו אז - בעוד המזהה האנונימי ומזהה המכשיר שהיא נאלצה ליצור לא מחליפים את השמורים, וביטולים וכתיבות שממתינים בתור משתי ההפעלות נשמרים. אם ההפעלה ההיא התנתקה או חיברה משתמש אחר, המזהה האנונימי של המשתמש השמור לא עובר הלאה, והמאפיינים שלו שלא נשלחו לעולם לא נשלחים בשם המשתמש החדש - הם נמחקים אחרי התנתקות, ואחרת נשלחים תחת המזהים שלו עצמו. מפתח ראשי שנשאר לא שמיש - בשלוש הפעלות ברציפות שבהן אף אחסון לא נפתח בו, כשהסיבה מראה שהוא לא יכול לפענח או שהכשלים נמשכים לפחות יום - מוחלף במפתח חדש של ה-SDK עצמו (במפתח ה-androidx של האפליקציה שלכם לעולם לא נוגעים). האחסון המוצפן שתחת המפתח הישן נמחק אז, וכל מה שעוד היה בו ושההפעלות מהקובץ הרגיל לא כתבו מחדש - כולל ביטולים שממתינים בתור - אובד; אחסון שהמפתח הישן עדיין פותח מועבר קודם למפתח החדש. ה-main thread לעולם לא ממתין לניסיון חוזר. עם initializeAsync(), Activity שנפתחה בזמן שה-SDK עלה נקלטת - עבור הודעות in-app, בקשת ההרשאה בהפעלה ולחיצה על ההתראה שפתחה אותה. ה-callback שלכם להודעות in-app רץ ב-main thread, וסוג הודעה שגרסת ה-SDK הזו לא מכירה מגיע כ-MessageType.CUSTOM.
הודעות HTML נשארות במסמך שלהן. רק המסמך של ההודעה עצמה נטען. כל ניווט שהיא מתחילה - קישור, רענון, frame - מבוטל, וקישור שהמשתמש לוחץ עליו נפתח בדפדפן. ה-CSS שלה לא יכול לסגור את אלמנט ה-<style> שלו.
unregisterPush() נשאר בתוקף. הטוקן נשכח במכשיר מיד והבקשה נשמרת עד שהשרת מאשר אותה. ה-SDK לא רושם טוקן מעצמו שוב - לא בהפעלה הבאה ולא כש-Firebase מחליף את הטוקן - עד שתקראו ל-registerPushToken() או שהמשתמש יאשר הרשאה דרך requestPushPermission().
wipeData() מוחק את המזהה האנונימי ואת מזהה המשתמש, את תור האירועים, כתיבות ממתינות, מצב ה-In-App והיסטוריית ההצגות, את הסשן, את מזהה המכשיר ואת טוקן הפוש - ומבטל את רישום הטוקן בשרת, תחת הזהות שנמחקת. הוא שומר על הסרת הסכמה קיימת: מחיקת נתונים אינה הסכמה.
לאחר מכן הפוש נשאר כבוי עד שהאפליקציה קוראת שוב ל-registerPushToken או מבקשת הרשאה מחדש - טוקן שמגיע בהפעלה מאוחרת יותר לא נרשם אוטומטית.
הצגה אחת לכל טריגר. קמפיין נתפס ברגע שהוא נבחר, לפני כל השהיית הצגה, כך ששני טריגרים ברצף מהיר (אותו אירוע שנמדד פעמיים, סנכרון שמגיע בזמן שהודעה מושהית ממתינה) מציגים אותו פעם אחת. התפיסה מסתיימת כשההודעה נסגרת (גם כשאסור לשלוח דבר) או כשלא ניתן היה להציג אותה. שום הודעה לא מוצגת כל עוד ההסכמה אינה GRANTED או שהאדם ביטל מעקב. סנכרון שכבר היה בדרך כש-identify(), reset(), wipeData() או שינוי הסכמה התרחשו - נזרק, וסנכרון חדש מגיע אחריו.
תדירות In-App. רק דיווח displayed מנצל את מגבלת התדירות של הקמפיין ואת המרווח לפני הקמפיין הבא. הרנדרר של ה-SDK מדווח עליו כשההודעה מוצגת; עם setInAppMessageCallback אתם מדווחים עליו עם trackInAppImpression(campaignId, "displayed"). העברת הודעה ל-callback שלכם לא מנצלת דבר, כך שהודעה שהחלטתם לא להציג לא מעכבת את הבאה. clearInAppMessageCallback() מחזיר את הרינדור ל-SDK. Presenter שהותקן עם setInAppPresenter מדווח דרך ה-callback onEvent שהוא מקבל ("displayed", "clicked", "dismissed") ואסור לו לקרוא גם ל-trackInAppImpression, אחרת ההודעה נספרת פעמיים.
תיעוד הסרת ההסכמה. optOut() כותב את מאפיין הפרופיל tracking_opted_out = true (ללא קידומת $), ו-optIn() מחזיר אותו ל-false - כל אחד מהם רק כשהמתג באמת משתנה. רשומה שעדיין ממתינה בתור כשהמשתמש שלה מתנתק נשארת שלו ונמסרת לפרופיל שלו, ולעולם לא מופנית לאדם הבא.
תיעוד API
כל המתודות הציבוריות, עם שמות הפרמטרים שלהן. קריאות בצורה Joryio.x() הן סטטיות ובטוחות גם לפני האתחול; קריאות דרך Joryio.getInstance() דורשות SDK מאותחל.
// Setup
Joryio.initialize(context: Context, sdkKey: String?, apiHost: String?, config: JoryioConfig? = JoryioConfig()): Joryio
Joryio.initializeAsync(context: Context, sdkKey: String?, apiHost: String?, config: JoryioConfig? = JoryioConfig(), onReady: ((Joryio) -> Unit)? = null)
Joryio.isInitialized(): Boolean
Joryio.getInstance(): Joryio // throws IllegalStateException before initialize()
Joryio.whenReady(action: ((Joryio) -> Unit)?) // runs now, or as soon as the SDK is initialized
Joryio.SDK_VERSION: String
// Events
Joryio.track(eventName: String?, properties: Map<String, Any?>? = emptyMap())
Joryio.trackScreen(screenName: String?, properties: Map<String, Any?>? = emptyMap())
Joryio.flush()
Joryio.getInstance().ecommerce(config: EcommerceConfig? = null): EcommerceTracker
// Identity and attributes
Joryio.identify(userId: String?)
Joryio.addAlias(label: String?, name: String?) // works before identify() too
Joryio.reset() // logout: forgets the user, new anonymous id
Joryio.setAttribute(key: String?, value: Any?) // a null value removes the attribute
Joryio.setAttributes(attributes: Map<String, Any?>?)
Joryio.incrementAttribute(key: String?, by: Number? = 1)
Joryio.unsetAttribute(key: String?)
Joryio.getInstance().getAnonymousId(): String
Joryio.getInstance().getUserId(): String?
Joryio.getInstance().getSessionId(): String
Joryio.getInstance().getSessionInfo(): SessionInfo
Joryio.getInstance().getIdentity(): Pair<String?, String> // (userId, anonymousId)
// Tracking consent, opt-out and stored data
Joryio.optOut()
Joryio.optIn()
Joryio.isUserOptedOut(): Boolean // the opt-out switch only
Joryio.updateConsent(consent: TrackingConsent?) // GRANTED, DENIED or PENDING
Joryio.getTrackingConsent(): TrackingConsent? // before initialize(): the stored decision, or GRANTED when there is none
Joryio.getInstance().getTrackingConsent(): TrackingConsent
Joryio.wipeData() // safe before initialize()
// Marketing subscription
Joryio.setSubscription(channel: String?, status: String?)
Joryio.addToSubscriptionGroup(groupId: String?, channel: String?)
Joryio.removeFromSubscriptionGroup(groupId: String?, channel: String?)
Joryio.unregisterPush()
// In-app messages
Joryio.getInstance().setInAppMessageCallback(callback: ((InAppCampaign) -> Unit)?, capabilities: List<String>? = null)
Joryio.clearInAppMessageCallback() // hands rendering back to the SDK
Joryio.getInstance().setInAppPresenter(presenter: InAppMessagePresenter?)
Joryio.syncInAppCampaigns()
Joryio.getInstance().trackInAppImpression(campaignId: String?, action: String?, elementId: String? = null)
Joryio.getInstance().submitInAppForm(campaignId: String?, values: Map<String, Any?>?, onResult: ((String) -> Unit)?)
Joryio.resetDisplayedCampaigns() // testing only: needs enableDebug
Joryio.getInstance().evaluateInAppCampaigns() // testing only: needs enableDebug
// Push
Joryio.getInstance().registerPushToken(token: String?, onResult: ((PushRegistrationResult) -> Unit)? = null)
Joryio.getInstance().unregisterPush()
Joryio.getInstance().isPushEnabled(): Boolean
Joryio.getInstance().getDeviceToken(): String?
Joryio.getInstance().getPushPermissionStatus(): PushPermissionStatus
Joryio.getInstance().requestPushPermission(activity: Activity?, callback: ((PushPermissionStatus) -> Unit)? = null)
Joryio.getInstance().reportPushDelivered(trackingId: String?) // only with your own messaging service
Joryio.getInstance().trackPushClick(trackingId: String?) // only when you handle the tap yourself
// SDK authentication
Joryio.setSdkAuthenticationToken(token: String?)
Joryio.setSdkAuthenticationErrorHandler(handler: ((SdkAuthError) -> Unit)?)
// Diagnostics
Joryio.getInstance().getQueueSize(): Int
Joryio.getInstance().apiHost: String
Joryio.getInstance().currentApiEndpoint: String
Joryio.getInstance().lastTransportError: JoryioTransportError?
פתרון תקלות
אירועים לא מופיעים
- ודאו שמפתח ה־SDK נכון:
jry_sdk_android_* - הפעילו יומני ניפוי שגיאות:
enableDebug = true - בדקו logcat לשגיאות
- ודאו הרשאות רשת ב־manifest
- קראו
flush()לשליחה מיידית
פוש לא עובד
- ודאו ש־Firebase הוגדר כראוי
- בדקו שקובץ ה־service account JSON של Firebase הועלה ללוח הבקרה
- ודאו שטוקן המכשיר נרשם
- בדקו דרך Firebase Console קודם
- חפשו את השגיאה מ־שלח push לבדיקה בקבלת אישורי Firebase (FCM)
שגיאות בנייה
- ודאו מינימום SDK גרסה 23
- בצעו Sync לתלויות Gradle
- נקו ובנו מחדש את הפרויקט
בניות Release (R8) וגיבוי
- R8 / ProGuard: שני ה-artifacts מגיעים עם כללים משלהם, שמוחלים אוטומטית כשהאפליקציה מגדירה
isMinifyEnabled = true(כולל R8 full mode). אין צורך להוסיף כללים, ואפליקציה ללאfirebase-messagingעדיין נבנית - הפוש פשוט כבוי. - גיבוי: לא נדרש דבר. תור האירועים נמצא בתיקיית ה-no-backup של האפליקציה, ואחסון מוצפן ששוחזר מגיבוי (שלעולם אינו כולל את מפתח ה-Keystore) מזוהה ונוצר מחדש, עדיין מוצפן. אם האפליקציה מצהירה על כללי גיבוי משלה ואתם רוצים להחריג במפורש את קובצי ה-SDK, הפנו אל
@xml/joryio_backup_rules(android:fullBackupContent) ואל@xml/joryio_data_extraction_rules(android:dataExtractionRules), או העתיקו את השורות שלהם לכללים שלכם. ה-SDK לא מגדיר את התכונות האלה בעצמו, ולכן הוא לעולם לא מתנגש עם ה-manifest שלכם.
מעקב מסחר אלקטרוני
Android SDK כולל מעקב מובנה אחר מסחר אלקטרוני לאירועי מוצרים, עגלה, תשלום והזמנות. ראו את מדריך מעקב המסחר האלקטרוני המשותף לכל ה-SDKs לתיעוד המלא עם דוגמאות Kotlin.
רכישה, עם המפתחות שדוחות ההכנסה קוראים:
import io.joryio.sdk.ecommerce.EcommerceCartItem
import io.joryio.sdk.ecommerce.EcommerceOrder
Joryio.getInstance().ecommerce().purchase(
EcommerceOrder(
orderId = "ORD-1001",
items = listOf(EcommerceCartItem(productId = "SKU-1", name = "Blue Shirt", price = 29.99, quantity = 2)),
value = 59.98,
currency = "USD",
)
)
זה שולח Order Completed עם orderId ו-total. אם אתם עוקבים אחרי רכישה ידנית, השתמשו בשני המפתחות האלה: הזמנה שנשלחת עם order_id או value נרשמת ללא הכנסה.
רכישה ללא orderId, עם value שאינו מספר סופי, או עם פריט ללא productId - מתעלמים ממנה עם שורה ביומן, והיא לעולם לא נרשמת כהזמנה בסך אפס; אף מתודה של ה-tracker לא זורקת חריגה. EcommerceTracker.create() עובד לפני initialize(): האירועים שלו ממתינים ל-SDK.
ממשקי בדיקה ואבחון
שתי קבוצות, וההבדל ביניהן חשוב.
ממשקי בדיקה - מתעלמים מהם אלא אם enableDebug פעיל. הם משנים מצב חי, ולכן
קריאה מקרית בגרסת פרודקשן הייתה פוגמת במגבלות תדירות ובדיווח אמיתיים. כשמצב הדיבאג
כבוי הם רושמים אזהרה ולא עושים דבר.
| מתודה | מה היא עושה |
|---|---|
resetDisplayedCampaigns() | שוכחת אילו הודעות in-app כבר הוצגו, כדי שיוכלו להופיע שוב. מצב התדירות נשמר במכשיר - זה מה שמאפשר טריגר מיידי וגם עבודה לא מקוונת - ולכן שינוי הקמפיין בצד השרת לא יגרום להצגה חוזרת. זו הדרך היחידה לבדוק שוב בלי להתקין מחדש. |
evaluateInAppCampaigns() | מריצה מחדש את החלטת ההצגה על הקמפיינים שכבר סונכרנו, בלי פנייה לרשת. |
ממשקי אבחון - זמינים תמיד, גם בפרודקשן. הם רק קוראים מצב ולכן אינם יכולים להזיק, ומסך תמיכה זקוק להם דווקא כשמשהו משתבש בשטח.
| מתודה | על מה היא עונה |
|---|---|
getQueueSize() | כמה אירועים ממתינים לשליחה? |
currentApiEndpoint / lastTransportError | מול איזה שרת אנחנו עובדים, והאם הקריאה האחרונה נכשלה? |
getIdentity(), getSessionInfo() ו-getDeviceToken() אינן שייכות לאף אחת מהקבוצות - הן API רגיל. קריאת הזהות שתחתיה המכשיר מדווח היא פעולה לגיטימית לאפליקציה (העברת טוקן לשרת שלכם, הצגת מזהה תמיכה במסך החשבון).
צעדים הבאים
- iOS SDK - שילוב SDK ל־iOS
- Web SDK - שילוב Web SDK
- מדריך Push Notifications - למידה על קמפיינים של פוש
- מדריך In-App Campaigns - יצירת הודעות בתוך האפליקציה
- אירועים מותאמים - מעקב אחרי אירועים מותאמים
- E-Commerce API
מנוי שיווקי
האם מותר לשלוח הודעות לאדם הזה בערוץ מסוים. זו הסכמה שונה מ-optOut(), שעניינה האם מותר לעקוב אחריו - אחת אינה גוררת את השנייה, ואפשר לסרב למעקב ועדיין לרצות את הניוזלטר.
זה מה שמרכז העדפות בתוך האפליקציה כותב.
Joryio.setSubscription("email", "unsubscribed")
Joryio.addToSubscriptionGroup("list_abc123", "email")
ערוצים: email, sms, whatsapp, push, viber. סטטוסים: optedIn, subscribed, unsubscribed.
נדרש מזהה - הקריאה מדולגת, עם שגיאה ביומן, אם אין מזהה משתמש ואין מזהה אנונימי.
ביטול מנוי תמיד עובד. setSubscription(channel, "unsubscribed") ו-removeFromSubscriptionGroup מתקבלים ונשלחים גם כשהאדם ביטל מעקב (opt-out), כשההסכמה היא DENIED או PENDING, ואחרי wipeData() - בקשה להפסיק לקבל הודעות לעולם לא מעוכבת ולא נמחקת. הרשמה (subscribed, optedIn, addToSubscriptionGroup) נדחית בזמן opt-out או סירוב, ומוחזקת כל עוד ההסכמה ממתינה. שינוי חדש יותר של אותו ערוץ או אותה קבוצה מחליף שינוי ישן יותר שעוד ממתין לשליחה, ושינוי שנעשה לפני identify() ונשלח אחריו נושא גם את מזהה המשתמש.