نظرة عامة على LiveData   جزء من Android Jetpack.

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

تُعتبر فئة مراقب LiveData، التي تمثّلها فئة Observer، في حالة نشطة إذا كانت دورة حياتها في حالة STARTED أو RESUMED. لا تُرسِل LiveData إشعارات إلا للمراقِبين النشطين بشأن التعديلات. لا يتم إشعار المراقِبين غير النشطين المُسجَّلين لمشاهدة مواد عرض LiveData بالتغييرات.

يمكنك تسجيل مراقب مقترن بعنصر ينفذ واجهة LifecycleOwner. تسمح هذه العلاقة بإزالة المراقب عندما تتغيّر حالة كائن Lifecycle المقابل إلى DESTROYED. ويُعدّ ذلك مفيدًا بشكل خاص للأنشطة والمقاطع لأنّه يمكنها مراقبة عناصر LiveData بأمان ولا داعي للقلق بشأن عمليات التسرب، إذ يتم إلغاء اشتراك الأنشطة والمقاطع على الفور عند إتلاف دورات حياتها.

لمزيد من المعلومات حول كيفية استخدام LiveData، راجِع مقالة العمل مع مثيلات LiveData.

مزايا استخدام LiveData

يوفّر استخدام LiveData المزايا التالية:

ضمان تطابق واجهة المستخدم مع حالة بياناتك
تتبع LiveData نمط المراقب. تُرسِل فئة LiveData إشعارًا إلى عناصر Observer عند تغيُّر البيانات الأساسية. يمكنك توحيد الرمز البرمجي لتعديل واجهة المستخدم في عناصر Observer هذه. بهذه الطريقة، لن تحتاج إلى تعديل واجهة المستخدم في كل مرّة تتغيّر فيها بيانات التطبيق لأنّ أداة المراقبة ستُجري ذلك نيابةً عنك.
عدم تسرّب الذاكرة
ترتبط مراقبي الأداء بعناصر Lifecycle، ويقومون بتنظيف البيانات بعد انتهاء دورة نشاطها.
عدم حدوث أعطال بسبب الأنشطة المتوقفة
إذا كانت دورة حياة المُراقِب غير نشطة، مثل نشاط في الحزمة الخلفية، لن يتلقّى أي أحداث LiveData.
التوقف عن التعامل اليدوي مع دورة الحياة
ترصد مكونات واجهة المستخدم البيانات ذات الصلة فقط ولا تتوقف عن الرصد أو تستأنفه. تدير LiveData كل هذا تلقائيًا لأنّها على دراية بتغييرات حالة دورة الحياة ذات الصلة أثناء المراقبة.
بيانات محدَّثة دائمًا
إذا أصبحت رحلة المستخدِم غير نشطة، ستتلقّى أحدث البيانات عند عودتها إلى النشاط مرة أخرى. على سبيل المثال، يتلقّى النشاط الذي كان في الخلفية أحدث البيانات مباشرةً بعد عودته إلى المقدّمة.
تغييرات الإعداد المناسبة
في حال إعادة إنشاء نشاط أو جزء بسبب تغيير في الإعدادات، مثل تبديل وضع الجهاز، يتلقّى على الفور أحدث البيانات المتاحة.
مشاركة الموارد
يمكنك توسيع LiveData العنصر باستخدام نمط العنصر الفردي لتضمين خدمات النظام حتى يمكن مشاركتها في تطبيقك. يتصل عنصر LiveData بخدمة النظام مرة واحدة، ثم يمكن لأي مراقب يحتاج إلى المورد مشاهدة LiveData العنصر فقط. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة توسيع نطاق LiveData.

العمل مع عناصر LiveData

اتّبِع الخطوات التالية للعمل مع عناصر LiveData:

  1. أنشئ مثيلًا من LiveData لتخزين نوع معيّن من البيانات. يتم ذلك عادةً في صف ViewModel.
  2. أنشئ عنصر Observer يحدِّد onChanged() الطريقة التي تتحكّم في ما يحدث عند تغيير البيانات المخزّنة في عنصر LiveData. يتم عادةً إنشاء عنصر Observer في وحدة تحكّم في واجهة المستخدم، مثل نشاط أو جزء.
  3. اربط كائن Observer بكائن LiveData باستخدام الطريقة observe(). تأخذ الطريقة observe() عنصرًا من نوع LifecycleOwner. يؤدي ذلك إلى اشتراك عنصر Observer في عنصر LiveData لكي يتم إعلامه بالتغييرات. يتم عادةً إرفاق عنصر Observer في ملف عنصر تحكّم في واجهة المستخدم، مثل نشاط أو جزء.

عند تعديل القيمة المخزّنة في عنصر LiveData، يتم تشغيل جميع المراقبين المسجّلين ما دام LifecycleOwner المرفق في الحالة النشطة.

تسمح LiveData لمراقبي عنصر التحكّم في واجهة المستخدم بالاشتراك في آخر الأخبار. عندما تتغيّر البيانات التي يحتفظ بها عنصر LiveData، يتم تعديل واجهة المستخدم تلقائيًا استجابةً لذلك.

إنشاء عناصر LiveData

‫LiveData هو عنصر تغليف يمكن استخدامه مع أي بيانات، بما في ذلك الكائنات التي تنفِّذ Collections، مثل List. يتم عادةً تخزين كائن LiveData ضمن كائن ViewModel ويتم الوصول إليه من خلال طريقة جلب، كما هو موضّح في المثال التالي:

Kotlin

class NameViewModel : ViewModel() {

    // Create a LiveData with a String
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

    // Rest of the ViewModel...
}

Java

public class NameViewModel extends ViewModel {

    // Create a LiveData with a String
    private MutableLiveData<String> currentName;

    public MutableLiveData<String> getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData<String>();
        }
        return currentName;
    }

    // Rest of the ViewModel...
}

في البداية، لا يتمّ ضبط البيانات في عنصر LiveData.

يمكنك قراءة المزيد من المعلومات عن مزايا فئة ViewModel وكيفية استخدامها في دليل ViewModel.

مراقبة عناصر LiveData

في معظم الحالات، تكون طريقة onCreate() لمكوّن التطبيق هي المكان المناسب لبدء مراقبة ملف شخصي على LiveData للأسباب التالية:

  • لضمان عدم إجراء النظام لطلبات متكرّرة من طريقة onResume() في النشاط أو القطعة
  • لضمان أن يحتوي النشاط أو المقتطف على بيانات يمكن عرضها فورًا بعد أن يصبح نشطًا بعد أن يصبح مكوّن التطبيق في حالة STARTED ، يتلقّى أحدث قيمة من عناصر LiveData التي يراقبها. ولا يحدث ذلك إلّا إذا تم ضبط عنصر LiveData المطلوب رصده.

بشكل عام، لا تُرسِل LiveData آخر المعلومات إلا عند تغيير البيانات، ولا تُرسِلها إلا إلى المراقبين النشطين. يُستثنى من هذا السلوك أنّ المراقبين يتلقّون أيضًا إشعارًا عند تغيير حالتهم من غير نشطة إلى نشطة. بالإضافة إلى ذلك، إذا تغيّر حالة المراقِب من غير نشط إلى نشط للمرة الثانية، لن يتلقّى سوى تعديل إذا تغيّرت القيمة منذ آخر مرة أصبح فيها نشطًا.

يوضّح الرمز البرمجي النموذجي التالي كيفية بدء مراقبة LiveData عنصر:

Kotlin

class NameActivity : AppCompatActivity() {

    // Use the 'by viewModels()' Kotlin property delegate
    // from the activity-ktx artifact
    private val model: NameViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Other code to setup the activity...

        // Create the observer which updates the UI.
        val nameObserver = Observer<String> { newName ->
            // Update the UI, in this case, a TextView.
            nameTextView.text = newName
        }

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.currentName.observe(this, nameObserver)
    }
}

Java

public class NameActivity extends AppCompatActivity {

    private NameViewModel model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Other code to setup the activity...

        // Get the ViewModel.
        model = new ViewModelProvider(this).get(NameViewModel.class);

        // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                nameTextView.setText(newName);
            }
        };

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.getCurrentName().observe(this, nameObserver);
    }
}

بعد استدعاء observe() مع تمرير nameObserver كمَعلمة ، يتم استدعاء onChanged() على الفور لتوفير أحدث قيمة مخزّنة في mCurrentName. إذا لم يضبط عنصر LiveData قيمة في mCurrentName، لن يتم استدعاء onChanged().

تعديل عناصر LiveData

لا تتوفّر في LiveData طرق متاحة للجميع لتعديل البيانات المخزّنة. تعرض فئة MutableLiveData الطريقتَين setValue(T) و postValue(T) علنًا، ويجب استخدامهما إذا كنت بحاجة إلى تعديل القيمة المخزّنة في عنصر LiveData. عادةً ما يتم استخدام MutableLiveData في ViewModel، ثم يعرِض ViewModel كائنات LiveData غير قابلة للتغيير للمراقبين فقط.

بعد إعداد علاقة المراقب، يمكنك بعد ذلك تعديل قيمة العنصر LiveData، كما هو موضّح في المثال التالي الذي يؤدي إلى تنشيط جميع المراقبين عندما ينقر المستخدم على زر:

Kotlin

button.setOnClickListener {
    val anotherName = "John Doe"
    model.currentName.setValue(anotherName)
}

Java

button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        String anotherName = "John Doe";
        model.getCurrentName().setValue(anotherName);
    }
});

يؤدي استدعاء setValue(T) في المثال إلى استدعاء المراقبين لطريقة onChanged() بالقيمة John Doe. يعرض المثال الضغط على زر، ولكن يمكن استدعاء setValue() أو postValue() لتعديل mName لأسباب مختلفة، بما في ذلك استجابةً لطلب شبكة أو اكتمال تحميل قاعدة بيانات. وفي جميع الحالات، يؤدي استدعاء setValue() أو postValue() إلى تنشيط مراقبي الأداء وتعديل واجهة المستخدم.

استخدام LiveData مع Room

تتوافق مكتبة الثبات في Room مع طلبات البحث القابلة للتتبّع، والتي تعرض كائنات LiveData. يتم كتابة طلبات البحث القابلة للتتبّع كجزء من كائن الوصول إلى قاعدة البيانات (DAO).

ينشئ Room كل الرمز البرمجي اللازم لتعديل عنصر LiveData عند تعديل قاعدة بيانات. يُشغِّل الرمز الذي تم إنشاؤه طلب البحث بشكل غير متزامن على سلسلسة مهام في background عند الحاجة. يُعدّ هذا النمط مفيدًا للحفاظ على مزامنة البيانات التي يتم عرضها في واجهة مستخدِم مع البيانات المخزّنة في قاعدة بيانات. يمكنك الاطّلاع على مزيد من المعلومات حول Room وDAO في دليل مكتبة Room الثابتة.

استخدام وظائف التشغيل المتعدّد في وقت واحد مع LiveData

يتضمّن الإصدار LiveData دعمًا لعمليات التشغيل المتعدّدة في Kotlin. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة استخدام مهام Kotlin المتعدّدة المهام مع مكونات بنية Android.

LiveData في بنية التطبيق

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

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

قد يكون من المغري استخدام عناصر LiveData في فئة طبقة البيانات، ولكن LiveData غير مصمّمة لمعالجة مصادر البيانات غير المتزامنة. على الرغم من أنّه يمكنك استخدام عمليات تحويل LiveData وMediatorLiveData لتحقيق ذلك، إلا أنّ هذا النهج له عيوب: إنّ إمكانية دمج تدفّقات البيانات محدودة جدًا ويتم رصد جميع عناصر LiveData (بما في ذلك العناصر التي تم إنشاؤها من خلال عمليات التحويل) في سلسلة المهام الرئيسية. الرمز البرمجي أدناه هو مثال على كيفية حظر السلسلة الأساسية بواسطة عقد LiveData في Repository:

Kotlin

class UserRepository {

    // DON'T DO THIS! LiveData objects should not live in the repository.
    fun getUsers(): LiveData<List<User>> {
        ...
    }

    fun getNewPremiumUsers(): LiveData<List<User>> {
        return getUsers().map { users ->
            // This is an expensive call being made on the main thread and may
            // cause noticeable jank in the UI!
            users
                .filter { user ->
                  user.isPremium
                }
          .filter { user ->
              val lastSyncedTime = dao.getLastSyncedTime()
              user.timeCreated > lastSyncedTime
                }
    }
}

Java

class UserRepository {

    // DON'T DO THIS! LiveData objects should not live in the repository.
    LiveData<List<User>> getUsers() {
        ...
    }

    LiveData<List<User>> getNewPremiumUsers() {
    return Transformations.map(getUsers(),
        // This is an expensive call being made on the main thread and may cause
        // noticeable jank in the UI!
        users -> users.stream()
            .filter(User::isPremium)
            .filter(user ->
                user.getTimeCreated() > dao.getLastSyncedTime())
            .collect(Collectors.toList()));
    }
}

إذا كنت بحاجة إلى استخدام مصادر بيانات في طبقات أخرى من تطبيقك، ننصحك باستخدام عمليات تدفق Kotlin ثم تحويلها إلى LiveData في ViewModel باستخدام asLiveData(). اطّلِع على مزيد من المعلومات حول استخدام Flow مع LiveData في هذه الدورة التدريبية حول الترميز. بالنسبة إلى قواعد البيانات التي تم إنشاؤها باستخدام Java، ننصحك باستخدام المنفِّذين بالاشتراك مع وظائف الاستدعاء أو RxJava.

توسيع نطاق LiveData

تُعتبر أداة LiveData أنّ المراقب في حالة نشطة إذا كانت مرحلته المتعلّقة بالرصد في حالتَي STARTED أو RESUMED. يوضّح الرمز البرمجي النموذجي التالي كيفية توسيع فئة LiveData:

Kotlin

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }
}

Java

public class StockLiveData extends LiveData<BigDecimal> {
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    public StockLiveData(String symbol) {
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
        stockManager.removeUpdates(listener);
    }
}

يتضمّن تنفيذ مستمع الأسعار في هذا المثال الطرق المهمة التالية:

  • يتمّ استدعاء الطريقة onActive() عندما يكون لكائن LiveData مراقب نشط. وهذا يعني أنّك تحتاج إلى بدء مراقبة تعديلات أسعار الأسهم من خلال هذه الطريقة.
  • يتمّ استدعاء الطريقة onInactive() عندما لا يتضمّن عنصر LiveData أيّ مراقبين نشطين. بما أنّه لا يوجد مراقبون يستمعون إلى المكالمة، ما مِن سبب للبقاء متصلاً بخدمة StockManager.
  • تعمل الطريقة setValue(T) على تعديل قيمة مثيل LiveData وإرسال إشعار إلى أي مراقبين فعالين بشأن التغيير.

يمكنك استخدام فئة StockLiveData على النحو التالي:

Kotlin

public class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val myPriceListener: LiveData<BigDecimal> = ...
        myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })
    }
}

Java

public class MyFragment extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        LiveData<BigDecimal> myPriceListener = ...;
        myPriceListener.observe(getViewLifecycleOwner(), price -> {
            // Update the UI.
        });
    }
}

تُرسِل الطريقة observe() العنصر LifecycleOwner المرتبط بعرض المقتطف كوسيطة أولى. ويشير ذلك إلى أنّه يكون هذا المُراقب مرتبطًا بعنصر Lifecycle المرتبط بالمالك، أي:

  • إذا لم يكن عنصر Lifecycle في حالة نشطة، لن يتم استدعاء المُراقب حتى إذا تغيّرت القيمة.
  • بعد تدمير العنصر Lifecycle، تتم إزالة المراقب تلقائيًا.

بما أنّ عناصر LiveData تكون على دراية بالحالة، يمكنك مشاركتها بين أنشطة وشظايا وخدمات متعددة. للحفاظ على مثال بسيط، يمكنك تنفيذ فئة LiveData كعنصر وحيد على النحو التالي:

Kotlin

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager: StockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }

    companion object {
        private lateinit var sInstance: StockLiveData

        @MainThread
        fun get(symbol: String): StockLiveData {
            sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
            return sInstance
        }
    }
}

Java

public class StockLiveData extends LiveData<BigDecimal> {
    private static StockLiveData sInstance;
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    @MainThread
    public static StockLiveData get(String symbol) {
        if (sInstance == null) {
            sInstance = new StockLiveData(symbol);
        }
        return sInstance;
    }

    private StockLiveData(String symbol) {
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
        stockManager.removeUpdates(listener);
    }
}

ويمكنك استخدامها في المقتطف على النحو التالي:

Kotlin

class MyFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })

    }

Java

public class MyFragment extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> {
            // Update the UI.
        });
    }
}

يمكن لعدة أقسام وأنشطة مراقبة مثيل MyPriceListener. لا تتصل LiveData بخدمة النظام إلا إذا كان هناك خدمة واحدة أو أكثر مرئية ونشطة.

تحويل LiveData

قد تحتاج إلى إجراء تغييرات على القيمة المخزّنة في عنصر LiveData قبل إرساله إلى المراقبين، أو قد تحتاج إلى عرض مثيل مختلف LiveData استنادًا إلى قيمة مثيل آخر. تقدِّم حزمة Lifecycle الفئة Transformations التي تتضمّن طرق مساعدة تتيح هذه السيناريوهات.

Transformations.map()
تطبّق دالة على القيمة المخزّنة في عنصر LiveData، ويؤدي ذلك إلى نشر النتيجة في مسار الإحالة الناجحة.

Kotlin

val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<String> = userLiveData.map {
    user -> "${user.name} ${user.lastName}"
}

Java

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});
Transformations.switchMap()
على غرار map()، تطبِّق دالة على القيمة المخزّنة في LiveData العنصر وتزيل تغليفه وتُرسِله إلى أسفل السلسلة. يجب أن تعرِض الدالة التي تم تمريرها إلى switchMap() عنصر LiveData، كما هو موضّح في المثال التالي:

Kotlin

private fun getUser(id: String): LiveData<User> {
  ...
}
val userId: LiveData<String> = ...
val user = userId.switchMap { id -> getUser(id) }

Java

private LiveData<User> getUser(String id) {
  ...;
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

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

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

Kotlin

class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {

    private fun getPostalCode(address: String): LiveData<String> {
        // DON'T DO THIS
        return repository.getPostCode(address)
    }
}

Java

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
       this.repository = repository;
    }

    private LiveData<String> getPostalCode(String address) {
       // DON'T DO THIS
       return repository.getPostCode(address);
    }
}

بعد ذلك، يجب أن يُلغي مكوّن واجهة المستخدم تسجيله من عنصر LiveData السابق ويُسجِّله في المثيل الجديد في كل مرة يُطلِب فيها getPostalCode(). بالإضافة إلى ذلك، في حال إعادة إنشاء مكوّن واجهة المستخدم، يؤدي ذلك إلى إجراء طلب آخر إلى الأسلوب repository.getPostCode() بدلاً من استخدام نتيجة الطلب السابق.

بدلاً من ذلك، يمكنك تنفيذ البحث عن الرمز البريدي كتحويل لإدخال العنوان، كما هو موضّح في المثال التالي:

Kotlin

class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
    private val addressInput = MutableLiveData<String>()
    val postalCode: LiveData<String> = addressInput.switchMap {
            address -> repository.getPostCode(address) }


    private fun setInput(address: String) {
        addressInput.value = address
    }
}

Java

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    private final MutableLiveData<String> addressInput = new MutableLiveData();
    public final LiveData<String> postalCode =
            Transformations.switchMap(addressInput, (address) -> {
                return repository.getPostCode(address);
             });

  public MyViewModel(PostalCodeRepository repository) {
      this.repository = repository
  }

  private void setInput(String address) {
      addressInput.setValue(address);
  }
}

في هذه الحالة، يتم تعريف الحقل postalCode على أنّه تحويل لمحاولة addressInput. ما دام تطبيقك يتضمّن مراقبًا نشطًا مرتبطًا بحقل postalCode، تتم إعادة احتساب قيمة الحقل واستردادها عند تغيُّر addressInput.

تسمح هذه الآلية للمستويات الدنيا من التطبيق بإنشاء عناصر LiveData يتم حسابها بشكلٍ كسول عند الطلب. يمكن لعنصر ViewModel الحصول بسهولة على مراجع إلى عناصر LiveData، ثم تحديد قواعد التحويل بالإضافة إلى هذه العناصر.

إنشاء عمليات تحويل جديدة

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

دمج مصادر LiveData متعددة

MediatorLiveData هو فئة فرعية من LiveData تسمح بدمج مصادر LiveData متعددة. بعد ذلك، يتم تنشيط مراقبي MediatorLiveData كائنات عند تغيُّر أي من كائنات مصدر LiveData الأصلية.

على سبيل المثال، إذا كان لديك عنصر LiveData في واجهة المستخدم يمكن تعديله من قاعدة بيانات محلية أو شبكة، يمكنك إضافة المصادر التالية إلى MediatorLiveData:

  • عنصر LiveData مرتبط بالبيانات المخزّنة في قاعدة البيانات
  • عنصر LiveData مرتبط بالبيانات التي يتم الوصول إليها من الشبكة

ما عليك سوى مراقبة عنصر MediatorLiveData لتلقّي ข้อมูล مثبّتة من كلا المصدرَين. للحصول على مثال مفصّل، اطّلِع على قسم الملحق: عرض حالة الشبكة في دليل بنية التطبيقات.

مصادر إضافية

لمزيد من المعلومات حول فئة LiveData، يمكنك الرجوع إلى المراجع التالية.

نماذج

  • Sunflower، تطبيق تجريبي يعرض أفضل الممارسات المتعلّقة بمكونات البنية

الدروس التطبيقية حول الترميز

المدوّنات

الفيديوهات