أحداث Android LiveData

تعد LiveData أداة رائعة لربط حالة بياناتك وكائناتك بدورة حياة (LifecycleOwner ، عادة ما تكون جزءًا أو نشاطًا).

عادةً ما يتم وضع LiveData في ViewModel ويستخدم لتحديث حالة واجهة المستخدم الخاصة بك. في الغالب ، يمكن لـ ViewModel البقاء على قيد الحياة على LifecycleOwner والحفاظ على حالة LiveData. هذه الآلية مناسبة عندما تحتاج إلى حفظ البيانات واستعادتها بعد مرور بعض الوقت ، على سبيل المثال ، بعد تغيير التكوين.

لكن ماذا لو أردنا استخدام آلية الأحداث ، وليس الدول؟ ومن الضروري في سياق دورة حياة المتصفح (LifecycleOwner). على سبيل المثال ، نحتاج إلى عرض رسالة بعد عملية غير متزامنة ، بشرط أن LifecycleOwner لا يزال على قيد الحياة ولديه متصفحات نشطة ومستعدة لتحديث واجهة المستخدم الخاصة به. إذا استخدمنا LiveData ، فسنستلم الرسالة نفسها بعد تغيير كل تكوين ، أو مع كل مشترك جديد. أحد الحلول التي تقترح نفسها هي معالجة البيانات في LiveData بعد معالجة البيانات في بعض المستعرضات.

على سبيل المثال ، رمز مثل هذا:

Observer { handle(it) yourViewModel.liveData.value = null } 

لكن هذا النهج له عدة عيوب ولا يلبي جميع المتطلبات الضرورية.

أرغب في الحصول على آلية حدث:

  1. يخطر فقط المشتركين النشطين
  2. في وقت الاشتراك لا يخطر البيانات السابقة ،
  3. لديه القدرة على ضبط العلم الذي تم التعامل معه على true لمقاطعة المعالجة الإضافية للحدث.

لقد طبقت فئة MutableLiveEvent ، والتي تحتوي على جميع الخصائص المذكورة أعلاه والتي يمكن أن تعمل مثل LiveData العادي.

كيفية الاستخدام:

 //   EventArgs        class MyIntEventArgs(data: Int) : EventArgs<Int>(data) //  viewModel class MainViewModel : ViewModel() { private val myEventMutable = MutableLiveEvent<MyIntEventArgs>() val myEvent = myEventMutable as LiveData<MyIntEventArgs> fun sendEvent(data: Int) { myEventMutable.value = MyIntEventArgs(data) } } val vm = ViewModelProviders.of(this).get(MainViewModel::class.java) vm.myEvent.observe(this, Observer { //   /* *   ,    , *      ,   handled = true */ it.handled = true }) 

كل الشفرة متاحة على GitHub ، وسأتحدث قليلاً عن التطبيق.

 class MutableLiveEvent<T : EventArgs<Any>> : MutableLiveData<T>() { internal val observers = ArraySet<PendingObserver<in T>>() @MainThread override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { val wrapper = PendingObserver(observer) observers.add(wrapper) super.observe(owner, wrapper) } override fun observeForever(observer: Observer<in T>) { val wrapper = PendingObserver(observer) observers.add(wrapper) super.observeForever(observer) } @MainThread override fun removeObserver(observer: Observer<in T>) { when (observer) { is PendingObserver -> { observers.remove(observer) super.removeObserver(observer) } else -> { val pendingObserver = observers.firstOrNull { it.wrappedObserver == observer } if (pendingObserver != null) { observers.remove(pendingObserver) super.removeObserver(pendingObserver) } } } } @MainThread override fun setValue(event: T?) { observers.forEach { it.awaitValue() } super.setValue(event) } } 

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

 internal class PendingObserver<T : EventArgs<Any>>(val wrappedObserver: Observer<in T>) : Observer<T> { private var pending = false override fun onChanged(event: T?) { if (pending && event?.handled != true) { pending = false wrappedObserver.onChanged(event) } } fun awaitValue() { pending = true } } 

في PendingObserver ، يتم تعيين العلامة المعلقة إلى false بشكل افتراضي. هذا يحل البند 2 (لا تخطر البيانات القديمة) من قائمتنا.

والرمز في MutableLiveEvent

 override fun setValue(event: T?) { observers.forEach { it.awaitValue() } super.setValue(event) } 

يتم أولاً تعيين "انتظار" على "صحيح" وعندها فقط يتم تحديث البيانات داخل نفسه. هذا يضمن تنفيذ المطالبة 1. (تنبيه فقط المشتركين النشطين).

النقطة الأخيرة التي لم أتحدث عنها حتى الآن هي EventArgs. هذه الفئة هي تعميم توجد فيه إشارة تم التعامل معها لمقاطعة المعالجة الإضافية للحدث (الفقرة 3).

 open class EventArgs<out T>(private val content: T?) { var handled: Boolean = false val data: T? get() { return if (handled) { null } else { content } } } 

هذا كل شيء ، شكرا لمشاهدة!

Source: https://habr.com/ru/post/ar468749/


All Articles