סנכרון דלתא לנתונים לא מקוונים במובייל: מדריך יישום מלא

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

פיתוח ותמיכה בכל סוגי היישומים הניידים:

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

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 1734 השירותים
סנכרון דלתא לנתונים לא מקוונים במובייל: מדריך יישום מלא
מורכב
מ- 1 שבוע עד 3 חודשים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית מובייל עבור FEEDME
    פיתוח אפליקציית מובייל עבור FEEDME
    971
  • פיתוח אפליקציית מובייל עבור XOOMER
    פיתוח אפליקציית מובייל עבור XOOMER
    869
  • פיתוח אפליקציית מובייל עבור RHL
    פיתוח אפליקציית מובייל עבור RHL
    1289
  • פיתוח אפליקציית מובייל עבור ZIPPY
    פיתוח אפליקציית מובייל עבור ZIPPY
    1143
  • פיתוח אפליקציית מובייל עבור Affhome
    פיתוח אפליקציית מובייל עבור Affhome
    1076
  • פיתוח אפליקציית מובייל עבור חברת FLAVORS
    פיתוח אפליקציית מובייל עבור חברת FLAVORS
    677

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

ללא סנכרון תקין, שינויים לא מקוונים אובדים, נוצרים קונפליקטים וחוויית המשתמש נפגעת. הגישה שלנו היא סנכרון דלתא דו-כיווני עם תור פעולות וטיפול בקונפליקטים. מקרה אמיתי: עבור אפליקציית מסחר אלקטרוני עם 500,000 מוצרים ו-10,000 הזמנות ביום, יישמנו סנכרון דלתא. קונפליקטים התרחשו ב-2% מההזמנות — נפתרו באמצעות LWW. זמן הסנכרון הממוצע ירד מ-30 ל-2 שניות (פי 15 מהר יותר), והתעבורה ירדה ב-90% (הפחתה של פי 10). סנכרון דלתא עולה על סנכרון מלא פי 10 במהירות ופי 50 בתעבורה.

כיצד ליישם סנכרון דלתא דו-כיווני?

סנכרון דלתא ממזער ביעילות את התעבורה. בניגוד לסנכרון מלא, הלקוח טוען רק שינויים מאז הסנכרון האחרון. זה מפחית את עומס השרת ב-90% ומאיץ את הסנכרון פי 5–10.

שיטה תעבורה זמן עומס שרת מתאים ל
סנכרון מלא גבוהה איטי גבוה נתונים קטנים (<100 רשומות)
סנכרון דלתא נמוכה מהיר נמוך שינויים תכופים, רשומות רבות
סנכרון מצטבר בינונית בינוני בינוני נתונים עם מזהים מונוטוניים

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

קוד SyncManager
data class SyncRequest(
    val lastSyncTimestamp: Long,
    val clientId: String
)

data class SyncResponse(
    val serverTimestamp: Long, // время ответа сервера
    val updated: List<ProductDto>, // изменённые или новые
    val deletedIds: List<String> // удалённые на сервере
)

בצד הלקוח, אנו מאחסנים data class SyncRequest( val lastSyncTimestamp: Long, val clientId: String ) data class SyncResponse( val serverTimestamp: Long, // время ответа сервера val updated: List<ProductDto>, // изменённые или новые val deletedIds: List<String> // удалённые на сервере ) ב-MMKV או SharedPreferences. הסנכרון הבא משתמש בו כמסנן.

מדוע חותמת הזמן של השרת חשובה?

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

ארכיטקטורת SyncManager

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

קוד SyncManager (kotlin)
class SyncManager(
    private val api: SyncApi,
    private val dao: ProductDao,
    private val pendingOpsDao: PendingOperationDao,
    private val prefs: SyncPreferences
) {
    suspend fun sync(): SyncResult {
        // 1. Отправляем накопленные offline-операции
        val pending = pendingOpsDao.getAll()
        if (pending.isNotEmpty()) {
            try {
                val uploadResult = api.uploadOperations(pending.map { it.toRequest() })
                pendingOpsDao.deleteByIds(uploadResult.processedIds)
            } catch (e: NetworkException) {
                return SyncResult.NetworkError
            }
        }

        // 2. Скачиваем изменения с сервера
        return try {
            val response = api.sync(
                SyncRequest(
                    lastSyncTimestamp = prefs.lastSyncTimestamp,
                    clientId = prefs.clientId
                )
            )
            dao.applyDelta(
                updated = response.updated.map { it.toEntity() },
                deletedIds = response.deletedIds
            )
            prefs.lastSyncTimestamp = response.serverTimestamp
            SyncResult.Success(
                updatedCount = response.updated.size,
                deletedCount = response.deletedIds.size
            )
        } catch (e: Exception) {
            SyncResult.Error(e)
        }
    }
}

lastSuccessfulSyncTimestamp מתבצע בעסקה — אטומית. או הכל או כלום:

@Transaction
suspend fun applyDelta(updated: List<ProductEntity>, deletedIds: List<String>) {
    upsertAll(updated)
    softDeleteByIds(deletedIds, System.currentTimeMillis())
}

מחיקה רכה היא חובה: אנחנו לא מוחקים פיזית, אנו מגדירים דגל class SyncManager( private val api: SyncApi, private val dao: ProductDao, private val pendingOpsDao: PendingOperationDao, private val prefs: SyncPreferences ) { suspend fun sync(): SyncResult { // 1. Отправляем накопленные offline-операции val pending = pendingOpsDao.getAll() if (pending.isNotEmpty()) { try { val uploadResult = api.uploadOperations(pending.map { it.toRequest() }) pendingOpsDao.deleteByIds(uploadResult.processedIds) } catch (e: NetworkException) { return SyncResult.NetworkError } } // 2. Скачиваем изменения с сервера return try { val response = api.sync( SyncRequest( lastSyncTimestamp = prefs.lastSyncTimestamp, clientId = prefs.clientId ) ) dao.applyDelta( updated = response.updated.map { it.toEntity() }, deletedIds = response.deletedIds ) prefs.lastSyncTimestamp = response.serverTimestamp SyncResult.Success( updatedCount = response.updated.size, deletedCount = response.deletedIds.size ) } catch (e: Exception) { SyncResult.Error(e) } } } ושומרים חותמת זמן. אחרת, סנכרון הדלתא הבא "ישכח" את המחיקה הזו שוב.

טריגרים לסנכרון

הסנכרון מופעל במספר תרחישים:

class SyncScheduler(
    private val workManager: WorkManager,
    private val syncManager: SyncManager,
    private val networkMonitor: NetworkMonitor
) {
    init {
        val periodicSync = PeriodicWorkRequestBuilder<SyncWorker>(15, TimeUnit.MINUTES)
            .setConstraints(Constraints(requiredNetworkType = NetworkType.CONNECTED))
            .build()
        workManager.enqueueUniquePeriodicWork(
            "periodic-sync",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicSync
        )
    }

    fun observeNetworkAndSync() {
        networkMonitor.isOnline
            .filter { it }
            .distinctUntilChanged()
            .onEach { triggerImmediateSync() }
            .launchIn(applicationScope)
    }

    fun onAppForeground() {
        val lastSync = prefs.lastSyncTimestamp
        val tooOld = System.currentTimeMillis() - lastSync > 5 * 60 * 1000L
        if (tooOld) triggerImmediateSync()
    }
}

ב-iOS, המקבילה ל-WorkManager היא applyDelta ו-@Transaction suspend fun applyDelta(updated: List<ProductEntity>, deletedIds: List<String>) { upsertAll(updated) softDeleteByIds(deletedIds, System.currentTimeMillis()) } (ראו תיעוד Apple). לפי מדריך ביצוע רקע של Apple, משימות רקע ב-iOS מוגבלות ל-30 שניות. הפתרון שלנו מכבד את המגבלות הללו ומשתמש בטריגרים אופטימליים.

סנכרון תמונות וקבצים

נתונים בינאריים מטופלים בנפרד ממטא-דאטה. אנו מסנכרנים רשימת קבצים (שמות, כתובות URL, hashes) ומורידים קבצים באמצעות בקשות בודדות עם עדיפות:

class MediaSyncManager {
    suspend fun syncMedia(mediaList: List<MediaMeta>) {
        val toDownload = mediaList.filter { meta ->
            !fileCache.exists(meta.localPath) || fileCache.getHash(meta.localPath) != meta.serverHash
        }
        toDownload.chunked(4).forEach { batch ->
            batch.map { meta -> async { downloadFile(meta) } }.awaitAll()
        }
    }
}

נתחים של 4 — אל תעמיסו על החיבור; במקרה של ניתוק, לכל היותר 4 קבצים מהאצווה הנוכחית אובדים.

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

אסטרטגיה עיקרון ביצועים מורכבות
LWW (הכותב האחרון מנצח) השינוי האחרון לפי זמן השרת מנצח מהיר מאוד נמוכה
CRDT מיזוג אוטומטי ללא קונפליקטים בינוני גבוהה
מיזוג מותאם אישית פתרון ידני דרך ממשק משתמש איטי גבוהה

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

סטטוס סנכרון בממשק המשתמש

המשתמש צריך לראות את טריות הנתונים. מינימום: חותמת זמן של הסנכרון האחרון. עדיף: אייקון סטטוס (מסונכרן / מסנכרן / שגיאת סנכרון) ליד נתונים שעלולים להיות מיושנים.

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

מה כלול

  • ביקורת ארכיטקטורת הנתונים הנוכחית והכנת מפרט
  • עיצוב סכמת סנכרון (ישויות, מזהים, קונפליקטים)
  • יישום SyncManager, תור פעולות ואינטגרציית API
  • הגדרת משימות רקע (WorkManager / BGAppRefreshTask)
  • פיתוח מטפל קונפליקטים (LWW או CRDT)
  • כיסוי קוד בבדיקות יחידה ותרחישי בדיקות אינטגרציה
  • תיעוד API ומדריך פריסה
  • תמיכה ל-30 יום לאחר ההשקה

תהליך

  1. ניתוח — לימוד מודל הנתונים, עומס, דרישות עקביות
  2. עיצוב — בחירת אסטרטגיית קונפליקטים, הגדרת שדות סנכרון
  3. יישום — כתיבת קוד, אינטגרציה עם הקצה האחורי שלכם (REST, GraphQL, Firebase)
  4. בדיקות — הדמיית תרחישים לא מקוונים, בדיקות עומס, אימות מקרי קצה
  5. פריסה — הגדרת CI/CD, ניטור, רישום שגיאות סנכרון

לוח זמנים ועלות

יישום מלא של סנכרון דלתא דו-כיווני עם תור פעולות וטיפול בקונפליקטים: 4 עד 8 שבועות בהתאם לנפח הנתונים ומספר הישויות. העלות מתחילה מ-$10,000 וממוצעת ב-$25,000 עבור רוב הפרויקטים. יישום סנכרון דלתא יכול להפחית עלויות תשתית שרת בעד 80% ולהאיץ את זמן היציאה לשוק ב-2 חודשים. עבור לקוח דומה במסחר אלקטרוני, חסכנו $40,000 בשנה בעלויות שרת.

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

הצוות שלנו של מפתחי iOS ואנדרואיד מוסמכים מבטיח עמידה בהנחיות App Store ו-Google Play. ניסיון בפרויקטים מ-50,000 עד 10 מיליון משתמשים. סנכרון דלתא הוא ההתמחות המרכזית שלנו, וביצענו אותו למעלה מ-30 פעמים — הוא מהיר פי 10 מסנכרון מלא ויעיל פי 50.