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

الإعلانات

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

 

تسهل عليك مكتبة الترحيل تحميل البيانات تدريجياً وبأمان ضمن دالة RecyclerView الخاصه بالتطبيق.

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

مكتبة الترحيل تساعد تطبيقك، على مراقبة وعرض، مجموعة فرعية معقولة، من هذه البيانات. ولهذه الوظيفة العديد من المزايا:

– طلبات البيانات، تستهلك نطاق ترددي أقل، للشبكة وموارد أقل للنظام.

المستخدمون الذين لديهم خطط بيانات مقننه، أو صغيرة، سيقّدرون هذه التطبيقات المدركة للبيانات.

– حتى أثناء تحديث وإنعاش البيانات، يستمر التطبيق في الإستجابة بسرعة، لإدخالات المستخدم.

إذا كان تطبيقك، يتضمن بالفعل منطقاً لبيانات الترحيل وعرض القوائم، فقد قدمنا إرشادات حول كيفية تحديث تطبيقك الحالي.

 

ملاحظة: لإستيراد مكتبة الترحيل إلى مشروع اندرويد، راجع إضافة مكونات إلى مشروعك.

 

يقدم هذا الدليل، نظرة عامة حول كيفية إستخدام مكتبة الترحيل، لطلب وعرض البيانات التي يرغب المستخدمون في رؤيتها..

 

أثناء إستهلاك موارد النظام، بشكلٍ إقتصادي أكثر. بالنسبة للأدلة المحددة لطبقات بنية تطبيقك، أطلع على الصفحات التالية:

 

ملاحظة: تساعدك مكتبة الترحيل على عرض البيانات في حاويات “قائمة واجهة المستخدم” الخاصة بك بسلاسة..

بغض النظر، عما إذا كنت تستخدم قاعدة بيانات، داخلية فقط، للجهاز أو تقوم بإحضار معلومات من خلفية “backend” تطبيقك.

لمعرفة المزيد حول أفضل طريقة لإستخدام المكتبة، بناءً على مكان وجود بيانات تطبيقك، راجع دعم بنى مختلفة للبيانات.

 

 

 

 

 

بنية المكتبة


المكون الأساسي لمكتب الترحيل هو فئة PagedList، وهي عبارة عن مجموعة تحمّل أجزاء من بيانات تطبيقك، أو صفحات، بشكلٍ غير متزامن.

هذه الفئة تخدم كوسيط بين الأجزاء الأخرى من بنية تطبيقك:

 

البيانات

يقوم كل مثيل من PagedList بتحميل لقطة محدّثة، لبيانات تطبيقك من مصدر البيانات DataSource الخاص به.

تتدفق البيانات الخلفية أو من قاعدة بيانات التطبيق إلى كائن PagedList.

تدعم مكتبة الترحيل مجموعة متنوعة من بنى التطبيقات، بما في ذلك قاعدة بيانات مستقلة وقاعدة بيانات تتصل بخادم الخلفية.

لمعرفة المزيد، راجع مكونات وإعتبارات البيانات.

 

 

 

واجهة المستخدم

الفئة PagedList تعمل مع PagedListAdapter لتحميل العناصر إلى RecyclerView.

تعمل هذه الفئات معاً لجلب وعرض المحتوى أثناء تحميله، وجلب المحتوى خارج العرض وتحريك تغييرات المحتوى.

لمعرفة المزيد، راجع مكونات وإعتبارات واجهة المستخدم.

 

مكتبة الترحيل تقوم بتنفيذ نمط المراقب الموصى به، في دليل بنية التطبيق.

على وجه الخصوص، المكونات الأساسية للمكتبة تقوم بإنشاء مثيل <LiveData<PagedList (أو الفئات المكافئه المستنده إلى RxJava2 .. )..

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

مع مراعاة دورات حياة وحدات تحكم واجهة مستخدمك.

 

 

 

 

 

دعم بنى بيانات مختلفة


مكتبة الترحيل تدعم بنية التطبيق، بما في ذلك، البُنى التي يجلب فيها تطبيقك، البيانات فقط من خادم المصدر الأساسي “backend”..

أو يجلبها فقط من قاعدة بيانات موجودة على الجهاز، أو من مزيج من المصدرين. يقدم هذا القسم توصيات لكل حالة من هذه الحالات.

لقد قدمنا أمثلة، على الأنماط الموصى بها، لإستخدامها في بنى البيانات المختلفة. لعرضها، راجع نموذج PagingWithNetwork على موقع GitHub.

 

 

 

الشبكة فقط

لعرض البيانات من خادم المصدر الأساسي، إستخدم الإصدار المتزامن من  Retrofit API لتحميل المعلومات إلى كائن DataSource المخصص الخاص بك.

ملاحظة: كائنات DataSource في مكتبة الترحيل لا تقوم بتقديم أي معالجة للأخطاء لأن التطبيقات المختلفة..

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

راجع نموذج PagingWithNetwork للإطلاع على مثال عن هذا السلوك.

 

 

 

قاعدة البيانات فقط

قم بإعداد RecyclerView الخاصة بك، لمراقبة التخزين المحلي، يفضل إستخدام مكتبة ثبات الروم.

وبهذه الطريقة، متى ما تم إدخال البيانات أو تعديلها، في قاعدة البيانات الخاصه بالتطبيق، تنعكس هذه التغييرات تلقائياً في RecyclerView والذي يعرض هذه البيانات.

 

 

 

 

الشبكة وقاعدة البيانات

بعد بدئك بمراقبة قاعدة البيانات، يمكنك الإستماع عندما تكون قاعدة البيانات خالية من البيانات بإستخدام PagedList.BoundaryCallback.

يمكنك بعد ذلك، جلب المزيد من العناصر، من شبكتك وإدراجها في قاعدة البيانات.

إذا كانت واجهة المستخدم الخاصة بك، تراقب قاعدة البيانات، فهذا هو كل ما تحتاج إلى القيام به.

يعرض مقتطف الكود التالي مثالاً على إستخدام إستدعاء الحدود boundary:

KOTLIN

class ConcertViewModel {
    fun search(query: String): ConcertSearchResult {
        val boundaryCallback =
                ConcertBoundaryCallback(query, myService, myCache)
        // Use a LiveData object to communicate your network's state back
        // to your app's UI, as in the following example. Note that error
        // handling isn't shown in this snippet.
        // val loadingState: LiveData<MyNetworkState> =
        //        boundaryCallback.loadingState
    }
}

class ConcertBoundaryCallback(
        private val query: String,
        private val service: MyService,
        private val cache: MyLocalCache
) : PagedList.BoundaryCallback<Concert>() {
    // Requests initial data from the network, replacing all content currently
    // in the database.
    override fun onZeroItemsLoaded() {
        requestAndReplaceInitialData(query)
    }

    // Requests additional data from the network, appending the results to the
    // end of the database's existing data.
    override fun onItemAtEndLoaded(itemAtEnd: Concert) {
        requestAndAppendData(query, itemAtEnd.key)
    }
}

JAVA

public class ConcertViewModel {
    public ConcertSearchResult search(String query) {
        ConcertBoundaryCallback boundaryCallback =
                new ConcertBoundaryCallback(query, myService, myCache);
        // Use a LiveData object to communicate your network's state back
        // to your app's UI, as in the following example. Note that error
        // handling isn't shown in this snippet.
        // LiveData<NetworkState> loadingState =
        //      boundaryCallback.getLoadingState();
    }
}

public class ConcertBoundaryCallback
        extends PagedList.BoundaryCallback<Concert> {
    private String mQuery;
    private MyService mService;
    private MyLocalCache mCache;

    public ConcertBoundaryCallback(String query, MyService service,
            MyLocalCache cache) {
        mQuery = query;
        // ...
    }

    // Requests initial data from the network, replacing all content currently
    // in the database.
    @Override
    public void onZeroItemsLoaded() {
        requestAndReplaceInitialData(mQuery);
    }

    // Requests additional data from the network, appending the results to the
    // end of the database's existing data.
    @Override
    public void onItemAtEndLoaded(@NonNull Concert itemAtEnd) {
        requestAndAppendData(mQuery, itemAtEnd.key);
    }
}

 

 

لمشاهدة مثال موسع، عن كيفية قيام “حلول” مكتبة الترحيل، بجلب البيانات من كلٍ من الشبكة وقاعدة البيانات..

إنتقل إلى كود إختبار الترحيل أو نموذج PagingWithNetwork على موقع GitHub.

 

 

 

 

التعامل مع أخطاء الشبكة


عند إستخدام شبكة، لإحضار أو تصفح البيانات، التي تقوم بعرضها بإستخدام مكتبة الترحيل، فمن المهم عدم معاملة الشبكة..

بأنها إما “متاحة” أو “غير متاحة” طوال الوقت، لأن العديد من الإتصالات تكون متقطعة أو غير مستقرة:

  • قد يفشل خادم معين في الإستجابة لطلب شبكة.
  • قد يكون الجهاز متصلاً بشبكة بطيئة أو ضعيفة.

 

بدلاً من ذلك، يجب أن يتحقق تطبيقك، من كل الطلبات الفاشله، ويقوم بالإستعادة بأمان قدر الإمكان، في الحالات التي تكون فيها الشبكة غير متاحة.

مثال، يمكنك توفير زر “إعادة المحاولة” للمستخدمين، لإختياره إذا كانت خطوة تحديث البيانات لا تعمل.

في حالة حدوث خطأ، أثناء خطوة ترحيل الصفحات، فمن الأفضل إعادة محاولة طلبات الترحيل تلقائياً.

 

 

 

تحديث تطبيق حالي

إذا كان تطبيقك، يستهلك بالفعل بيانات من قاعدة بيانات، أو من “مصدر خلفي backend source”..

فمن الممكن الترقية مباشرةً، إلى الوظائف التي توفرها مكتبة الترحيل. يوضح هذا القسم كيفية ترقية تطبيق له تصميم حالي شائع.

 

 

 

حلول الترحيل المخصصة

إذا كنت تستخدم وظيفة مخصصة، لتحميل مجموعات فرعية صغيرة من البيانات، من مصدر بيانات تطبيقك..

فيمكنك إستبدال هذا المنطق، بذلك المنطق من فئة PagedList. مثيلات PagedList توفر إتصالات مضمنة لمصادر البيانات الشائعة.

توفر هذه المثيلات أيضاً، محولات لكائنات RecyclerView التي قد تقوم بتضمينها في واجهة مستخدم التطبيق.

 

 

 

 

البيانات المحمله بإستخدام القوائم بدلاً من الصفحات

إذا كنت تستخدم قائمة في الذاكرة، كدعم لبنية البيانات، لمحول واجهة المستخدم، فخذ في الإعتبار مراقبة تحديثات البيانات..

بإستخدام فئة PagedList إذا كان عدد العناصر في القائمة، يمكن أن يصبح كبيراً. يمكن لمثيلات PagedList إما إستخدام..

 <LiveData<PagedList أو <Observable<List لتمرير تحديثات البيانات إلى واجهة مستخدم التطبيق، مما يقلل من أوقات التحميل وإستهلاك الذاكرة.

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

 

 

 

 

إقران مؤشر بيانات مع طريقة عرض قائمة بإستخدام “محول المؤشر” CursorAdapter

 

قد يستخدم تطبيقك محول المؤشر CursorAdapter لربط البيانات من المؤشر مع طريقة عرض القائمة ListView.

في هذه الحالة، ستحتاج عادةً إلى الترحيل من ListView إلى RecyclerView كحاوية لواجهة مستخدم التطبيق..

ثم إستبدال مكون المؤشر، إما بخاصية Room أو PositionalDataSource ، بناءً على ما إذا كانت مثيلات المؤشر، يمكنها الوصول إلى قاعدة بيانات SQLite أو لا.

في بعض الحالات، مثلما يحدث عند العمل مع مثيلات Spinner، تقوم فقط بتوفير المحول نفسه.

ثم تأخذ المكتبة البيانات، التي يتم تحميلها إلى ذلك المحول، وتعرض البيانات نيابة عنك. في هذه الحالات، قم بتغيير نوع بيانات المحول..

إلى <LiveData<PagedList ، ثم قم بلف هذه القائمة بكائن ArrayAdapter قبل محاولة جعل فئة المكتبة تقوم بنفخ هذه العناصر في واجهة المستخدم.

 

 

 

تحميل المحتوى بشكل غير متزامن بإستخدام AsyncListUtil

إذا كنت تستخدم كائنات AsyncListUtil لتحميل وعرض مجموعات من المعلومات بشكلٍ غير متزامن، فإن مكتبة الترحيل تتيح لك إمكانية تحميل البيانات بسهولة أكبر:

لا تحتاج بياناتك إلى أن تكون موضعية. تتيح لك مكتبة الترحيل إمكانية تحميل البيانات مباشرة..

من المصدر الأساسي “backend” بإستخدام المفاتيح التي توفرها الشبكة.

يمكن أن تكون بياناتك كبيرة بشكلٍ غير قابل للحصر. بإستخدام مكتبة الترحيل، يمكنك تحميل البيانات إلى صفحات حتى لا يتبقى أي بيانات.

يمكنك مراقبة بياناتك بسهولة أكبر. يمكن لمكتبة الترحيل تقديم بياناتك التي يحملها ViewModel الخاص بتطبيقك في بنية البيانات القابله للمراقبه.

 

ملاحظة: إذا كان تطبيقك الحالي يمكنه الوصول إلى قاعدة بيانات SQLite، فراجع القسم حول إستخدام مكتبة ثبات الروم.

 

 

 

 

 

 

أمثلة لقاعدة البيانات


تعرض مقتطفات الكود التالية، عدة طرق ممكنة، لتمكين جميع القطع من العمل معاً.

 

مراقبة البيانات المرحله بإستخدام LiveData

يعرض مقتطف الكود التالي كل القطع التي تعمل معاً. عندما تتم إضافة أحداث الحفلة الموسيقية أو إزالتها أو تغييرها في قاعدة البيانات..

يتم تحديث المحتوى في RecyclerView تلقائياً وبكفاءة:

KOTLIN

@Dao
interface ConcertDao {
    // The Int type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    fun concertsByDate(): DataSource.Factory<Int, Concert>
}

class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
    val concertList: LiveData<PagedList<Concert>> =
            LivePagedListBuilder(
                    concertDao.concertsByDate(), /* page size */ 20).build()
}

class ConcertActivity : AppCompatActivity() {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val viewModel = ViewModelProviders.of(this)
                .get(ConcertViewModel::class.java!!)
        val recyclerView = findViewById(R.id.concert_list)
        val adapter = ConcertAdapter()
        viewModel.concertList.observe(this, { pagedList ->
                adapter.submitList(pagedList) })
        recyclerView.setAdapter(adapter)
    }
}

class ConcertAdapter() :
        PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) {
    fun onBindViewHolder(holder: ConcertViewHolder, position: Int) {
        val concert = getItem(position)
        if (concert != null) {
            holder.bindTo(concert)
        } else {
            // Null defines a placeholder item - PagedListAdapter automatically
            // invalidates this row when the actual object is loaded from the
            // database.
            holder.clear()
        }
    }

    companion object {
        private val DIFF_CALLBACK = object :
                DiffUtil.ItemCallback<Concert>() {
            // Concert details may have changed if reloaded from the database,
            // but ID is fixed.
            override fun areItemsTheSame(oldConcert: Concert,
                    newConcert: Concert): Boolean =
                    oldConcert.id == newConcert.id

            override fun areContentsTheSame(oldConcert: Concert,
                    newConcert: Concert): Boolean =
                    oldConcert == newConcert
        }
    }
}

JAVA

@Dao
public interface ConcertDao {
    // The Integer type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    DataSource.Factory<Integer, Concert> concertsByDate();
}

public class ConcertViewModel extends ViewModel {
    private ConcertDao mConcertDao;
    public final LiveData<PagedList<Concert>> concertList;

    public ConcertViewModel(ConcertDao concertDao) {
        mConcertDao = concertDao;
        concertList = new LivePagedListBuilder<>(
            mConcertDao.concertsByDate(), /* page size */ 20).build();
    }
}

public class ConcertActivity extends AppCompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ConcertViewModel viewModel =
                ViewModelProviders.of(this).get(ConcertViewModel.class);
        RecyclerView recyclerView = findViewById(R.id.concert_list);
        ConcertAdapter adapter = new ConcertAdapter();
        viewModel.concertList.observe(this, adapter::submitList);
        recyclerView.setAdapter(adapter);
    }
}

public class ConcertAdapter
        extends PagedListAdapter<Concert, ConcertViewHolder> {
    protected ConcertAdapter() {
        super(DIFF_CALLBACK);
    }

    @Override
    public void onBindViewHolder(@NonNull ConcertViewHolder holder,
            int position) {
        Concert concert = getItem(position);
        if (concert != null) {
            holder.bindTo(concert);
        } else {
            // Null defines a placeholder item - PagedListAdapter automatically
            // invalidates this row when the actual object is loaded from the
            // database.
            holder.clear();
        }
    }

    private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<Concert>() {
        // Concert details may have changed if reloaded from the database,
        // but ID is fixed.
        @Override
        public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) {
            return oldConcert.getId() == newConcert.getId();
        }

        @Override
        public boolean areContentsTheSame(Concert oldConcert,
                Concert newConcert) {
            return oldConcert.equals(newConcert);
        }
    };
}

 

 

 

 

 

مراقبة البيانات المرحله بإستخدام RxJava2

إذا كنت تفضل إستخدام RxJava2 بدلاً من LiveData، فيمكنك بدلاً من ذلك إنشاء كائن Obsewable أو Flowable:

KOTLIN

class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
    val concertList: Flowable<PagedList<Concert>> =
            RxPagedListBuilder(concertDao.concertsByDate(), /* page size */ 50)
                    .buildFlowable(BackpressureStrategy.LATEST)
}

JAVA

public class ConcertViewModel extends ViewModel {
    private ConcertDao mConcertDao;
    public final Flowable<PagedList<Concert>> concertList;

    public ConcertViewModel(ConcertDao concertDao) {
        mConcertDao = concertDao;

        concertList = new RxPagedListBuilder<>(
                mConcertDao.concertsByDate(), /* page size */ 50)
                        .buildFlowable(BackpressureStrategy.LATEST);
    }
}

 

يمكنك بعد ذلك بدء وإيقاف مراقبة البيانات، بإستخدام مقتطف الكود التالي:

KOTLIN

class ConcertActivity : AppCompatActivity() {
    private lateinit var adapter: ConcertAdapter
    private lateinit var viewModel: ConcertViewModel

    private val disposable = CompositeDisposable()

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val recyclerView = findViewById(R.id.concert_list)
        viewModel = ViewModelProviders.of(this)
                .get(ConcertViewModel::class.java!!)
        adapter = ConcertAdapter()
        recyclerView.setAdapter(adapter)
    }

    override fun onStart() {
        super.onStart()
        disposable.add(viewModel.concertList.subscribe({
                flowableList -> adapter.submitList(flowableList)
        }))
    }

    override fun onStop() {
        super.onStop()
        disposable.clear()
    }
}

JAVA

public class ConcertActivity extends AppCompatActivity {
    private ConcertAdapter mAdapter;
    private ConcertViewModel mViewModel;

    private CompositeDisposable mDisposable = new CompositeDisposable();

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        RecyclerView recyclerView = findViewById(R.id.concert_list);

        mViewModel = ViewModelProviders.of(this).get(ConcertViewModel.class);
        mAdapter = new ConcertAdapter();
        recyclerView.setAdapter(mAdapter);
    }

    @Override
    protected void onStart() {
        super.onStart();
        mDisposable.add(mViewModel.concertList.subscribe(
                flowableList -> mAdapter.submitList(flowableList)
        ));
    }

    @Override
    protected void onStop() {
        super.onStop();
        mDisposable.clear();
    }
}

 

كود الـ ConcertDao و ConcertAdapter هما نفس الحل القائم على RxJava2 كما هو بالنسبة للحل المستند إلى LiveData.

 

 

 

 

تقديم التعليقات


شارك تعليقاتك وأفكارك معنا من خلال هذه المصادر:

تعقب المشاكل

قم بالإبلاغ عن المشاكل حتى نتمكن من إصلاح الأخطاء.

 

 

مصادر إضافية


لمعرفة المزيد حول مكتبة الترحيل، راجع الأدله ومقاطع الفيديو التالية:

  •  كود إختبار: تعلم كيفية إضافة مكتبة الترحيل إلى تطبيق، خطوة بخطوة.
  •  نموذج PagingWithNetwork: أنظر كيف تعمل مكتبة الترحيل مع واجهة برمجة تطبيقات المصدر الأساسي.
  •  جلسة Google I/O: شاهد هذا الفيديو الذي يقوم بتعريف مكتبة الترحيل.

 


للإطلاع على المقال باللغة الإنجليزية أضغط هنا.

الإعلانات