Kotlin / التزامن والتكامل والتصميم المترابط / الدرس 15
التمييز بين التدفق البارد والحالة الحالية المشتركة
استخدم تحويلات Flow وجمع قيمه، مع احترام الضغط العكسي والسياق والإلغاء ودمج StateFlow للتحديثات.
في هذه الصفحة
أهداف الدرس
- اشرح متى يبدأ التدفق البارد عمله ولماذا يختلف عنه StateFlow.
- اختر التخزين الوسيط أو دمج الحالة فقط عندما يحفظ تجاوز العمل معنى البيانات.
تصف Flow قيما تُنتَج عبر الزمن
تمثّل Flow قيما غير متزامنة باستخدام جمع يمكنه التعليق. يكون منشئ flow المعتاد باردا: تعمل كتلة المنتج عندما يجمع المستهلك التدفق، ويمكن لجمع مستقل أن ينفّذها مجددا. لا يبدأ مجرد إسناد التدفق عمله. يختلف ذلك عن List تحوي قيما بالفعل، وعن مصدر مشترك ساخن له عمر مستقل عن الجامعين. حدّد هل يكرر كل جمع طلبا مكلفا أو فعلا خارجيا. لا يعد نوع التدفق وحده بالدوام أو الإعادة أو أثر مرة واحدة تماما أو منتج واحد مشترك بين جميع المشتركين.
أبق المسار المتتابع البسيط سهلا للفحص
تحوّل العمليات الوسيطة وصف التدفق، بينما يطلب الجامع النهائي القيم. يستخدم المثال flowOf وmap وfilter وcollect دون تغييرات توقيت أو موزّع، ولذلك يتبع ترتيب الطباعة ترتيب المدخلات. هو ملف مصدر كامل يحتاج اعتماد kotlinx.coroutines متوافقا، لكنه لم يُترجم هنا. الحساب صغير عمدا؛ فالفكرة المهمة أن التحويل يعيد تدفق نتائج، لا قائمة قابلة للتعديل انتهت كل أعمالها بالفعل قبل بدء الجمع. يساعد إبقاء المسار بسيطا على قراءة العلاقة بين المدخل والقيم التي تصل إلى الجامع.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val values = flowOf(1, 2, 3)
.map { it * 10 }
.filter { it >= 20 }
values.collect { println(it) }
}
20
30
المخرجان المتوقعان هما 20 ثم 30. لا توجد عملية تخزين وسيط أو تزامن تغيّر ترتيب هذا المسار البسيط. لم تُنفّذ بيئة كوروتينات أو عملية ترجمة.
التخزين وإسقاط القيم اختيارات دلالية
قد يجعل التدفق البسيط الجامع البطيء يعلّق الإنتاج السابق له. تضيف buffer حدا بسلوك تخزين وسيط، ويمكن أن تتجاوز conflate قيما وسطية لصالح الأحدث، بينما تلغي collectLatest إجراء الجامع السابق عند وصول قيمة جديدة. ليست هذه مفاتيح سرعة متبادلة. قد يتقبل مؤشر تقدم تجاوز نسب وسيطة، بينما لا تتقبل ذلك أحداث مال أو حجز. حدّد السعة وسياسة تجاوزها حيث تدعمهما الواجهة، وتأكد من ألا يترك إلغاء عمل الجامع أثرا جانبيا غير قابل للعكس دون حساب.
تنشر StateFlow الحالة الحالية لا كل حدث
تكون StateFlow ساخنة، وتملك دائما قيمة حالية، وتستخدم دمجا قائما على المساواة. قد يتجاوز مشترك بطيء تحديثات وسيطة ويرى أحدث حالة، ولذلك لا تصلح سجلا دائما لكل حدث حجز. اعرض منظورا للحالة للقراءة فقط، وأبق التعديل داخل النموذج المالك لها. فضّل قيم بيانات غير قابلة للتعديل ونسخا جديدة للتحديث؛ فقد لا يؤدي تعديل قائمة داخل كائن الحالة نفسه إلى إصدار تحديث مميز كما تتوقع. يقرأ المثال القيمة النهائية مباشرة، ويتجنب برنامجا يجمع القيم إلى أجل غير مسمى.
import kotlinx.coroutines.flow.*
data class ScreenState(val selected: Int)
fun main() {
val mutable = MutableStateFlow(ScreenState(0))
val state: StateFlow<ScreenState> = mutable.asStateFlow()
mutable.update { current -> current.copy(selected = current.selected + 1) }
println(state.value.selected)
}
1
المخرج المتوقع 1. تحوّل update الحالة الحالية ذريّا، وقد تُقيَّم lambda الصرفة أكثر من مرة عند التنافس، لذلك أبعد الآثار الجانبية عنها. يحتاج المصدر kotlinx.coroutines، ولم يُنفّذ.
احفظ حدود السياق والإخفاق عبر التدفق
تمنع قواعد سياق Flow إصدار قيم اعتباطيا من سياق غير مرتبط داخل منشئ flow قياسي. تغيّر flowOn سياق تنفيذ الجزء السابق لها، ولا تغيّر سياق الجامع اللاحق. تعالج catch إخفاقات الجزء السابق وفقا لموضعها، ولا تلتقط بصورة عامة الاستثناءات التي يرميها الجمع اللاحق لها. حافظ على الإلغاء بدلا من تحويله إلى قيمة افتراضية. ويحتاج عمر الحالة الساخنة وسياسات المشاركة وجدولة الاختبارات تصميما خاصا. يقدم الدرس مخرجات متوقعة بالتحليل فقط؛ ولم يُنفّذ توقيت تدفق أو سباق مشتركين أو سلوك موزّع أو تحقق توافق مكتبة تشغيل.
الأخطاء الشائعة وإصلاحها
يُتوقع أن يبدأ الطلب بمجرد إنشاء Flow باردة.
- لماذا يحدث؟
- لا تعمل كتلة المنتج إلا عند جمع التدفق.
- كيف تصلحه؟
- اجمعه داخل نطاق مملوك، أو اختر دورة حياة صريحة لمشاركة مصدره.
- كيف تتجنب تكراره؟
- وثّق هل التدفق بارد أم ساخن عند حد تقديمه.
يفوّت مستهلك StateFlow أحداثا وسيطة مطلوبة.
- لماذا يحدث؟
- اختلط دمج الحالة الحالية بسجل أحداث يحتفظ بكل حدث.
- كيف تصلحه؟
- استخدم تصميما لتسليم الأحداث يطابق الاحتفاظ ودلالات التسليم المطلوبة.
- كيف تتجنب تكراره؟
- صنّف البيانات إلى حالة حالية أو أحداث فردية يجب عدم فقدها.
يتغير كائن حالة قابل للتعديل دون الإصدار المتوقع.
- لماذا يحدث؟
- أضعف التعديل المشترك في موضعه كشف التغيير القائم على المساواة.
- كيف تصلحه؟
- أنشئ قيمة حالة جديدة غير قابلة للتعديل وفقا لسياسة التحديث التي يملكها النموذج.
- كيف تتجنب تكراره؟
- تجنب المجموعات القابلة للتعديل داخل لقطات الحالة المنشورة.
اختبر فهمك
1. هل تناسب conflate سلسلة من أوامر إنقاص المقاعد؟
اعرض الحل المشروح
غالبا لا، لأن إسقاط أمر وسيط يغير الكمية والأثر التجاري. قد يناسب الدمج حالة عرض حالية، مثل أحدث تقدم، تكون الحالات الوسيطة فيها قابلة للاستبدال عن قصد. حدّد دلالات الحدث المطلوبة قبل اختيار عملية تتجاوز قيما.
2. لماذا يجب ألا ترسل lambda داخل MutableStateFlow.update رسالة بريد؟
اعرض الحل المشروح
قد تُقيّم update الدالة أكثر من مرة أثناء تنافس تحديثات متزامنة، فيتكرر الأثر الخارجي. أبق التحويل صرفا، ونفّذ الآثار عبر مسار ذي ملكية صريحة وهوية وسياسة أخطاء خاصتين به. ليس استبدال الحالة الذري معاملة ذرية مع مزود البريد.
أهم ما تعلمته
- تخدم Flow الباردة وStateFlow الساخنة والأحداث الدائمة عقودا مختلفة.
- يجب أن يحافظ التخزين الوسيط والإلغاء ودمج القيم على معنى البيانات.