انشاء المستودع بـ LiveData

Mohammad Laifمنذ سنة

بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته

في هذا الدرس سنتعلم كيفية انشاء كلاس مستودع للبيانات بالـ LiveData باستخدام نمط الـ Repository و الـ Singleton. حتى نهيئ المشروع لإنشاء قاعدة بيانات باستخدام الـ Room.

 

ماذا ستقرئ في هذا الدرس؟

  • ماهو نمط الـ Repository.
  • كيفية استخدام نمط الـ Repository.

 

ماهو الـ Repository؟

الـ Repository هو المسؤل عن جميع مصادر بياناتك, اي انك سوف تجمع كل بياناتك والـ Logic الخاص بهم في هذه الكلاس. سواء كنت تأتي بالبيانات من قواعد البيانات المحليه كالـ SQLite و Room او كنت تاتي بهم من الانترنت كـ Web Service او API او Cache. اي انه يعمل كطبقه وسط بين الـ ViewModel و قاعدة البيانات الـ Room او مصادر بياناتك الاخرى كالشبكة العنكبوتيه.

 

كيفية استخدام نمط الـ Repository

اولاً بداخل كلاس الـ Repository تقوم بحفظ جميع البيانات القادمة من شتى الطرق في الـ Room. ثم تطبيقك يتعامل فقط مع الـ Repository (يجب استخدام الربيستوري كنمط Singleton) لإخد البيانات منه. وهذا ما يسمى بالـ:

Single Source of Truth

اطلع على المصادر لمعرفة المزيد ورؤية مثال بسيط لكيفية العمل مع API.

 

ماذا سننشئ؟

  • كلاس Repository لعناصر الـ Subject.
  • كلاس Executors.


انشاء الـ Repository

نقوم بانشاء كلاس جديدة باسم SubjectRepository, وحقولها كالتالي: ننشئ عنصر منها (لاننا سنستخدمتها كـ Singleton) وحقل اخر للـ SubjectDao وحق للـ LiveData:

public class SubjectRepository {
    private static SubjectRepository sInstance;
    private final SubjectDao mSubjectDao;
    private LiveData<List<SubjectEntity>> mSubjects;
}

 

نقوم بانشاء Constructor يأخد Application (تستطيع جعله يأخد AppDatabase اذا احببت) من خلاله نقوم بعمل init لكل من حقولنا السابقه كـ لقاعدة البيانات و الـ Dao و حقل الـ mSubjects. 

    private SubjectRepository(Application application) {
        AppDatabase db = AppDatabase.getInstance(application);
        mSubjectDao = db.subjectDao();
        mSubjects = mSubjectDao.getAllSubject();

        // Other data sources
        /** Single source of truth
         * When we have another sources of data (persistent, model, web service, cache, etc...)
         * we should save them into room database. And we must read only from one source which is
         * our Room database with the help from LiveData.
         *
         * More info at (good examples fetching from API):
         * https://developer.android.com/jetpack/docs/guide#truth
         *
         * And, if we have two LiveData and we want to marge them we should use MediatorLiveData.
         */

    }

 

ثم ننشئ دالة تقوم بارجاع لنا هذه الكلاس كـ Singelton:

    public static SubjectRepository getInstance(Application application) {
        if (sInstance == null) {
            synchronized (SubjectRepository.class) {
                if (sInstance == null) {
                    sInstance = new SubjectRepository(application);
                }
            }
        }
        return sInstance;
    }

 

واخيراً نقوم بكتابة دوال التعامل مع البيانات ومعالجتهم كـ insert و update و delete و query. ملاحظة مهمة هنا الـ Repository سيقوم بعمل Delegate الى Dao وايضاً سيقوم بتشغيل هذه الدوال في الخلفية باستخدام Executor(سابقاً كنا نستخدم AsyncTask) والا سوف يتعطل التطبيق اذا قمنا بتشغيل هذه الدوال في الـ Main Thread:

    public LiveData<List<SubjectEntity>> getAllSubjects() {
        return mSubjects;
    }

    public LiveData<SubjectEntity> getSubjectById(final int subjectId) {
        return mSubjectDao.getSubjectById(subjectId);
    }

    public void insert(final SubjectEntity subjectEntity) {
        AppExecutors.getInstance().diskIO().execute(new Runnable() {
            @Override
            public void run() {
                mSubjectDao.insertSubject(subjectEntity);
            }
        });
    }

    public void update(final SubjectEntity subjectEntity) {
        AppExecutors.getInstance().diskIO().execute(new Runnable() {
            @Override
            public void run() {
                mSubjectDao.updateSubject(subjectEntity);
            }
        });
    }

    public void delete(final SubjectEntity subjectEntity) {
        AppExecutors.getInstance().diskIO().execute(new Runnable() {
            @Override
            public void run() {
                mSubjectDao.deleteSubject(subjectEntity);
            }
        });
    }

    public void deleteAll() {
        AppExecutors.getInstance().diskIO().execute(new Runnable() {
            @Override
            public void run() {
                mSubjectDao.deleteAll();
            }
        });
    }

تعقيب:

  • الكود سهل فقط عملية Delegate من هذه الكلاس (الـ Repository) الى الـ Dao مع تشغيل الدوال باستخدام AppExecutors.

 

كيفية عمل Executor؟

تستطيع عمل كلاس من نمط الـ Singelton لتزويدك بالـ Executors حتى تختصر كتابة للاكواد. او تستطيع استخدام الكلاس التي توفرها الجوجل كالتالي:

 

كلاس الـ AppExecutors:

package com.mzdhr.flashcards;

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;


/**
 * Global executor pools for the whole application.
 * <p>
 * Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind
 * webservice requests).
 */
public class AppExecutors {

    // For Singleton instantiation
    private static final Object LOCK = new Object();
    private static AppExecutors sInstance;
    private final Executor diskIO;
    private final Executor mainThread;
    private final Executor networkIO;

    private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
        this.diskIO = diskIO;
        this.networkIO = networkIO;
        this.mainThread = mainThread;
    }

    public static AppExecutors getInstance() {
        if (sInstance == null) {
            synchronized (LOCK) {
                sInstance = new AppExecutors(Executors.newSingleThreadExecutor(),
                        Executors.newFixedThreadPool(3),
                        new MainThreadExecutor());
            }
        }
        return sInstance;
    }

    public Executor diskIO() {
        return diskIO;
    }

    public Executor mainThread() {
        return mainThread;
    }

    public Executor networkIO() {
        return networkIO;
    }

    private static class MainThreadExecutor implements Executor {
        private Handler mainThreadHandler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
            mainThreadHandler.post(command);
        }
    }
}

هناك ايضاً مثال اخر للتعامل مع الـ API اطلع عليه من هنا اذا احببت.

في الدرس القادم سنعيد عمل بعض من هذه الخطوات لكن مع اختلافات, حتى نستطيع استخدام الـ Paging.

 

نهاية الدرس

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

 

المصادر:

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !