---
title: "الارتقاء بـ LiveData إلى مستوى أعلى مع MediatorLiveData وTransformations"
description: "مقدمة إلى MediatorLiveData وTransformations لبناء أنماط تفاعلية (reactive) باستخدام LiveData."
canonical: https://oozou.com/ar/blog/livedata-to-the-next-level-with-mediatorlivedata-and-transformations-31
locale: ar
date: 2018-12-07
author: "OOZOU Team"
---

# الارتقاء بـ LiveData إلى مستوى أعلى مع MediatorLiveData وTransformations

> مقدمة إلى MediatorLiveData وTransformations لبناء أنماط تفاعلية (reactive) باستخدام LiveData.

Published: 2018-12-07 · Author: OOZOU Team (Engineering) · 4 min read

**MediatorLiveData**

بحسب تعريفها في التوثيق، هي صنف فرعي (subclass) من LiveData يستطيع "مراقبة" كائنات LiveData أخرى، ويستدعي الدالة onChanged() عندما تتغير قيمة LiveData المراقَب.

```
LiveData<String> source = ....; //
final MediatorLiveData<String> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String x) {
                // do something here 
            }
        });
lang-java
```

في الكود أعلاه، عندما تتغير **_قيمة source_** تُستدعى الدالة **_onChanged_** الخاصة بالـ **Observer**.

**Transformations**

هذا الصنف أشبه بأداة مساعدة مبنية على MediatorLiveData. ويحتوي على دالتين فقط هما map() وswitchMap()

```
@MainThread
    public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
            @NonNull final Function<X, Y> func) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(func.apply(x));
            }
        });
        return result;
    }

   @MainThread
    public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
            @NonNull final Function<X, LiveData<Y>> func) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(trigger, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = func.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }
lang-java
```

تؤدي الدالتان الوظيفة نفسها: تطبّقان دالة على القيمة المخزنة في كائن LiveData، ثم تمرّران النتيجة إلى ما بعدها. الفرق أن **الدالة التي نمررها إلى switchMap() يجب أن تُرجع كائن LiveData.**

أنشأت مشروعًا تجريبيًا [هنا](https://github.com/jutikorn/TodoSameple). يمكنكم الاطلاع عليه ومتابعتي وأنا أشرح كيف يعمل الكود.

أستخدم [https://jsonplaceholder.typicode.com/](https://jsonplaceholder.typicode.com/) كواجهة API للاختبار مع الـ endpoint _/todos/1_. وأنشأت نموذجًا باسم **Todo** يطابق شكل الاستجابة

```
import com.google.gson.annotations.SerializedName

// https://jsonplaceholder.typicode.com/todo/1
class Todo(
    @SerializedName("userId") val userId: Int,
    @SerializedName("id") val id: Int,
    @SerializedName("title") val title: String,
    @SerializedName("completed") val completed: Boolean
)
lang-kotlin
```

وهناك صنفان إضافيان للتعامل مع استجابات الـ API.

```
import android.arch.lifecycle.LiveData

data class ResponseError(val code: Int? = null, val messages: String? = null)

data class ResponseResult<T>(val data: LiveData<T>, val error: LiveData<ResponseError>)
lang-kotlin
```

يحتوي ResponseResult على خاصيتين، كلتاهما من نوع LiveData: الأولى للاستجابة، والثانية للخطأ (ResponseError).

وبحسب توصيات Google، أنشأت وحدة repository تتولى جلب البيانات من الشبكة. وفي هذه الوحدة استخدمت حيلة صغيرة مع ResponseResult:

```
class TodoRepository(private val api: TodoApi) {

    fun getTodos(): ResponseResult<Todo> {
        val data = MutableLiveData<Todo>()
        val error = MutableLiveData<ResponseError>()
        api.getTodos().enqueue(object : Callback<Todo> {
            override fun onResponse(call: Call<Todo>, response: Response<Todo>) {
                if (response.isSuccessful) {
                    data.value = response.body()
                } else {
                    error.value = ResponseError(response.code(), response.errorBody()?.string())
                }
            }

            override fun onFailure(call: Call<Todo>, t: Throwable) {
                error.value = ResponseError()
            }
        })
        return ResponseResult(data, error)
    }
}
lang-kotlin
```

تُرجع الدالة getTodos() كائنًا من نوع `ResponseResult<Todo>` له خاصيتان: _قيمة data_ تتحدّث عند اكتمال الاستجابة، و _قيمة error_ تتحدّث عند حدوث خطأ.

والتزامًا بمبادئ SOLID، أبقيت صنف الـ Usecase بسيطًا جدًا.

```
class GetTodoUsecases(private val repository: TodoRepository) {

    operator fun invoke() = repository.getTodos()
}
lang-kotlin
```

**ربط كائنات LiveData ببعضها (Chaining)**

لنلقِ نظرة على صنفَي الـ ViewModel والـ Fragment:

```
class MainFragment: Fragment(){
    ...
    ...
    ...
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel._onActivityCreated.call()
    }
    ...
    ...
    ...
}

class MainViewModel(private val getTodoUsecase: GetTodoUsecases) : ViewModel() {

    val _onActivityCreated = SingleLiveEvent<Any>()

    private val responseResult: LiveData<ResponseResult<Todo>> = Transformations.map(_onActivityCreated) {
        getTodoUsecase()
    }

    val todoResult: LiveData<Todo> = Transformations.switchMap(responseResult) {
        it.data
    }

    val errorResult: LiveData<ResponseError> = Transformations.switchMap(responseResult) {
        it.error
    }
}
lang-kotlin
```

أطلقتُ كائن LiveData ([SingleLiveEvent](https://gist.github.com/jutikorn/0bf472de02480f0cf3b64bb4d438767a)) داخل _onActivityCreated()_. وإذا نظرتم إلى السطر 18 فستجدون أن _Transformation.map()_ تُستدعى، أي أن _getTodoUsecase()_ ستُستدعى. وبعد انتهاء الجلب تُسنَد القيمة إلى _responseResult_. وإذا نظرتم مجددًا إلى السطرين 22 و 26 فستجدون أن _Transformation.switchMap()_ تُستدعى، أي أنه عندما تتحدّث _قيمة responseResult_ سيشير الكائنان **_todoResult_** و **_errorResult_** إلى الكائنات نفسها التي تمثلها خصائص _responseResult_.

**نصيحة:** لن تُستدعى الدالة OnChanged() إذا لم يكن هناك أي مراقب (observer) لكائن MediatorLiveData.

المرجع:

[https://stackoverflow.com/questions/47575961/what-is-the-difference-between-map-and-switchmap-methods](https://stackoverflow.com/questions/47575961/what-is-the-difference-between-map-and-switchmap-methods)

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
