محتويات الدورة

تطوير Android / المتقدم: أنظمة محمولة وإصدارات موثوقة / الدرس 17

المزامنة أولوياً دون اتصال والتعارضات

ابنِ صندوق إرسال دائماً يحفظ قصد المستخدم عبر الانقطاع والمحاولات والتعديلات المتنافسة.

وقت القراءة التقريبي: 4 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • نمذج التعديلات المحلية والإقرار البعيد منفصلين.
  • عرّف سياسات التعارض والترتيب والحذف ومنع التكرار.

عرّف ما يعمل دون شبكة

يعني التصميم أولوياً دون اتصال عمل مجموعة محددة من المهام الأساسية بالبيانات المحلية، لا توفر كل ميزة بعيدة سحرياً. تقرأ الواجهة المصدر المحلي فوراً وتكشف الحداثة والتعليق بصدق. قد يقبل التعديل محلياً وتبقى المزامنة معلقة. ميّز القبول المحلي عن تأكيد الخادم ليفهم المستخدم الحالة. قد يمنع انتهاء المصادقة أو الحصص أو تحقق الخادم القبول النهائي، فيحتاج المنتج مسار تدخل مدعوماً بدلاً من وعد أبدي بنجاح كل تغيير مجدول.

ثبّت التعديل والنية معاً

يحفظ صندوق الإرسال العمليات المعلقة بجانب السجلات المحلية. تحتاج كل عملية معرّفاً ثابتاً ونطاق حساب ومعرّف سجل وحمولة أو مرجعاً وإصدار أساس مناسباً. اكتب التعديل والصف المعلق بمعاملة واحدة كي لا يفصلهما تعطل. تحدث الجدولة بعد التثبيت ويمكن للمصالحة تكرارها عند الانقطاع. احفظ هوية العملية عبر إعادة العامل. يجب أن تكفي حالة القاعدة للاستئناف دون وجود النشاط أو ViewModel أو دالة الشبكة السابقة.

kotlin تسجل معاملة واحدة البيانات والقصد
data class PendingEdit(
    val operationId: String, val accountId: String,
    val bookId: String, val title: String, val baseVersion: Long
)

suspend fun renameOffline(
    db: RoomDatabase, books: BooksDao, outbox: OutboxDao, edit: PendingEdit
) {
    db.withTransaction {
        books.rename(edit.bookId, edit.title)
        outbox.insert(edit)
    }
}

استخدم RoomDatabase وwithTransaction المتوافقين من androidx.room. تمثل BooksDao وOutboxDao عقود DAO معلقة يعرّفها التطبيق وتحتاج PendingEdit تحويل كيان موافقاً. تحقق من ملكية الحساب والعنوان قبل الحد ولا تجدوِل إلا بعد التثبيت الناجح.

طبّق الإقرار دون محو تعديلات أحدث

عندما يقبل الخادم عملية سجّل نتيجتها وأزل أو أكمل عملية الصندوق المحددة ذرياً. قد يعدل المستخدم العنوان مجدداً أثناء رفع الأول. استبدال الصف المحلي عشوائياً بالاستجابة الأولى قد يمحو النية الأحدث. تتبّع جيلاً محلياً أو سلسلة معلقة وطبّق الإقرارات شرطياً. يجب أن تعيد المحاولة بعد مهلة مبهمة معرّف العملية نفسه مع منع تكرار الخادم. لا تمنح فرادة العمل المحلي ذلك الضمان البعيد وحدها.

اجعل التعارض قرار منتج

يسمح إصدار الأساس للخادم بكشف تعديل جهاز آخر. التعارض ليس بالضرورة فشلاً مؤقتاً للشبكة ولا ينبغي تكراره دون تغيير للأبد. اختر سياسة تناسب البيانات: حل يدوي للملاحظات القيمة أو دمج حقول حيث يسمح المعنى أو قاعدة آخر كاتب موثقة لإعداد قليل الخطر. ساعات الأجهزة مرجع ترتيب غير موثوق. يحتاج الحذف غالباً علامات أو سجلات إصدار كي لا يحيي جهاز قديم محتوى برفع نسخة أقدم.

kotlin صنّف نتائج المزامنة صراحة
sealed interface UploadOutcome {
    data class Accepted(val serverVersion: Long) : UploadOutcome
    data class Conflict(val serverTitle: String, val serverVersion: Long) : UploadOutcome
    data object RetryLater : UploadOutcome
    data object SignInRequired : UploadOutcome
}

fun needsUserAction(outcome: UploadOutcome): Boolean = when (outcome) {
    is UploadOutcome.Conflict, UploadOutcome.SignInRequired -> true
    is UploadOutcome.Accepted, UploadOutcome.RetryLater -> false
}

يستخدم نموذج Kotlin صياغة data object المدعومة بالأدوات الحديثة. تحول الخدمة نتائج البروتوكول لهذه الحالات ويحفظ المستودع آثارها. تحتاج RetryLater تراجعاً محدوداً، بينما يحتاج التعارض والدخول مسار حل منفصلاً.

اختبر الانقطاع عند كل حد

اختبر قبل التثبيت المحلي وبعده وقبل الجدولة وبعد قبول الخادم وقبل الإقرار وبعد الإقرار وقبل مراقبة الواجهة. أضف تعارض جهازين وحذفاً يتبعه رفع قديم وتبديل الحساب وطابوراً أكبر من قدرة عامل واحد. استخدم بدائل حتمية لضبط الترتيب والفشل. راقب عمر الطابور والمحاولات والأخطاء الدائمة والتعارض دون تسجيل الحمولات الخاصة. ينبغي لمؤشر المزامنة عكس إقرار السجل الدائم لا اكتمال اتصال واحد أو فراغ قائمة في الذاكرة.

الأخطاء الشائعة وإصلاحها

يختفي عنوان أحدث بعد انتهاء رفع أقدم

لماذا يحدث؟
يستبدل الإقرار الحالة الحالية دون شرط.
كيف تصلحه؟
طبّق النتائج على الجيل أو سلسلة العمليات المطابقة فقط.
كيف تتجنب تكراره؟
اختبر التعديل أثناء كتابة سابقة قيد الإرسال.

تعود سجلات محذوفة بعد مزامنة جهاز آخر

لماذا يحدث؟
لا يملك الحذف تمثيلاً بإصدار.
كيف تصلحه؟
استخدم علامات حذف أو عقد خادم مكافئاً.
كيف تتجنب تكراره؟
اختبر الكتّاب القدامى مقابل الحذف.

يتكرر تعارض للأبد

لماذا يحدث؟
صُنّف التعارض الدلالي كإدخال وإخراج مؤقت.
كيف تصلحه؟
احفظ حالة تعارض ونفذ سياسة الحل المختارة.
كيف تتجنب تكراره؟
افصل إخفاقات النقل عن نتائج المجال.

اختبر فهمك

1. يثبت تغيير اسم محلياً ثم تموت العملية قبل إخطار WorkManager. اشرح التعافي.

اعرض الحل المشروح

يبقى صف الصندوق لأنه ثُبّت مع التعديل. في فرصة المصالحة المناسبة التالية تتسبب الصفوف المعلقة بإعادة الجدولة. لا يلزم استنتاج فعل المستخدم من دالة رد مفقودة. يحتاج ذلك تصميم مصالحة حقيقياً لا افتراض ذرية الجدولة وتثبيت القاعدة عبر نظامين.

2. يعدل جهازان الملاحظة الخاصة نفسها انطلاقاً من الإصدار 7. عرّف حلاً يمنع الفقد الصامت.

اعرض الحل المشروح

اقبل الأولى بإصدار خادم جديد وارفض الثانية بسبب عدم تطابق الأساس كتعارض. احفظ النص المحلي المعلق ونص الخادم واعرض مقارنة أو دمجاً ثم أرسل المحتوى المحسوم مقابل الإصدار الحالي بهوية عملية ثابتة جديدة.

أهم ما تعلمته

  • القبول دون اتصال والتأكيد البعيد حالتان مختلفتان.
  • يحفظ صندوق الإرسال القصد عبر إخفاقات مبهمة.
  • تحدد سياسات التعارض والحذف الاتساق.

المراجع الرسمية