المحملات

الإعلانات

المحملات

 

المحملات  Loaders

تم إيقاف المحملات بدءاً من أندرويد (API 28). يتمثل الخيار الموصى به للتعامل مع تحميل البيانات أثناء معالجة دورات حياة الأنشظة والشظايا..

في إستخدام مزيج من ViewModels و LiveData. تنجو ViewModels من تغييرات التكوين مثل المحملات ولكن بنسب أقل.

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

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

 لمراقبة التغييرات التي تطرأ على البيانات. تتاح أيضاً ViewModels و LiveData في الحالات التي لا يمكنك فيها الوصول إلى LoaderManager..

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

دون الحاجة إلى التعامل مع دورة حياة واجهة المستخدم. لمعرفة المزيد حول LiveData، راجع دليل LiveData ولمعرفة المزيد حول ViewModels راجع دليل ViewModel.

 

تتيح لك واجهة برمجة تطبيقات المحمل، تحميل البيانات من موفر محتوى أو من مصدر بيانات آخر لتعرض في FragmentActivity أو Fragment.

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

فحينئذٍ يجب عليك أولاً الأخذ في الإعتبار بعض المشكلات التي قد تواجهها عندما لا تتستخدم المحمل:

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

إجراءات الإستعلام البطيئة المحتمله من تسلسل واجهة المستخدم.

– إذا قمت بجلب البيانات من تسلسل آخر، ربما مع “مهمة المزامنة” AsyncTask ، فأنت مسؤول عن إدارة كل من التسلسل..

وتسلسل واجهة المستخدم من خلال الأنشطة المختلفة أو أحداث دورة حياة الشظية، مثل ()onDestroy وتغييرات التكوين.

 

يقوم المحمل بحل هذه المشاكل ويتضمن مزايا أخرى. مثال: المحملات

– يعمل المحمل على تسلسلات منفصلة لمنع واجهة المستخدم المعطوبة أو التي ??لا تستجيب.

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

– إستمرار المحمل ونتائج التخزين المؤقت عبر تغييرات التكوين لمنع الإستعلامات المكررة.

– يمكن للمحمل تنفيذ مراقب للشاشة من أجل التغييرات التي تحدث في مصدر البيانات الأساسي.

مثال، تقوم CursorLoader تلقائياً بتسجيل ContentObserver لتشغيل إعادة التحميل عند تغير البيانات.

 

 

 

 

ملخص واجهة برمجة تطبيقات المحمل


هناك العديد من الفئات والواجهات التي قد تكون معنية عند إستخدام المحملات في تطبيق ما. تم تلخيصها في هذا الجدول:

الفئه/الواجهه الوصف
LoaderManager

مدير المحمل

فئة مجردة، مرتبطة بـ FragmentActivity أو بـ Fragment لإدارة واحد أو أكثر

من مثيلات المحمل. يوجد LoaderManager واحد فقط في كل نشاط أو شظيه.

ولكن LoaderManager الواحد يمكنه إدارة عدة محملات.

لجلب LoaderManager، قم بإستدعاء ()getSupportLoaderManager من النشاط أو الشظيه.

لبدء تحميل البيانات من المحمل، قم بإستدعاء إما ()initLoader أو ()restartLoader.

يحدد النظام تلقائياً ما إذا كان هناك محمل له نفس المعرف integer ID موجود بالفعل

وسوف يقوم إما بإنشاء محمل جديد أو يعيد إستخدام محمل موجود.

LoaderManager.LoaderCallbacks

مدير المحمل.إستدعاءات المحمل

تحتوي هذه الواجهة على دوال إستدعاء والتي يتم إستدعائها، عند حدوث أحداث محمل.

تحدد الواجهة ثلاثة دوال إستدعاء:

  • (onCreateLoader(int, Bundle – تُستدعى عندما يحتاج النظام لإنشاء محمل جديد. 

يجب أن يقوم كودك بإنشاء كائن محمل ويقوم بإعادته إلى النظام.

  • (onLoadFinished(Loader<D>, D – تُستدعى عندما ينتهي المحمل من تحميل البيانات.

عادة، يجب أن يقوم كودك بعرض البيانات للمستخدم.

 

  • (<onLoaderReset(Loader<D – تُستدعى عندما يتم إعادة ضبط المحمل الذي تم إنشاؤه سابقاً..

عند إستدعاء (destroyLoader(int أو عند تدمير النشاط أو الشظيه،

وبالتالي تصبح بياناته غير متاحه.

يجب أن يقوم كودك بإزالة أي مراجع لديه، خاصة ببيانات المحمل.

عادة، يتم تنفيذ هذه الواجهة بواسطة النشاط أو الشظيه، ويتم تسجيلها عند إستدعاؤك لـ:

()initLoader أو ()restartLoader

Loader

المحمل

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

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

  • AsyncTaskLoader – محمل مجرد يقوم بتوفير AsyncTask لتنفيذ عمليات تحميل على تسلسل منفصل.
  • CursorLoader – فئة فرعية محدده من AsyncTaskLoader لتحميل البيانات بشكلٍ غير متزامن من ContentProvider.

تستعلم عن ContentResolver وتقوم بإرجاع مؤشر.

 

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

 

 

 

إستخدام المحملات في التطبيق


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

نشاط شظيه أو شظيه

– مثيل لمدير المحمل.

مؤشر المحمل لتحميل البيانات المدعومة من قبل موفر المحتوى.

بدلاً من ذلك، يمكنك تنفيذ الفئة الفرعية الخاصة بك من المحمل أو محمل مهمة المزامنة لتحميل البيانات من مصدر آخر.

– تنفيذ LoaderManager.LoaderCallbacks. هذا حيث تقوم بإنشاء محمل جديد وتقوم بإدارة مراجعك إلى محمل حالي.

– طريقة لعرض بيانات المحمل، مثل SimpleCursorAdapter.

– مصدر بيانات، مثل موفر المحتوى، عند إستخدام مؤشر المحمل.

 

 

بدء محمل

يدير LoaderManager واحدة أو أكثر من مثيلات Loader بداخل FragmentActivity أو Fragment. لا يوجد سوى LoaderManager واحد لكل نشاط أو شظية.

تقوم عادة بتجهيز المحمل ضمن دالة ()onCreate الخاصة بالنشاط، أو داخل دالة ()onActivityCreated الخاصة بالشظية. تفعل هذا على النحو التالي:

KOTLIN

supportLoaderManager.initLoader(0, null, this)

 

JAVA

// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getSupportLoaderManager().initLoader(0, null, this);

 

 

تأخذ دالة ()initLoader المعاملات “الباراميتر” التالية:

– معرف فريد يعرّف المحمل. في هذا المثال، المعرف هو 0.

– معطيات إختيارية لتزويد المحمل في البنيه (فارغة في هذا المثال).

– تنفيذ LoaderManager.LoaderCallbacks، الذي يستدعيه LoaderManager للإبلاغ عن أحداث المحمل.

في هذا المثال، تقوم الفئة المحلية بتنفيذ واجهة LoaderManager.LoaderCallbacks ، بحيث تقوم بتمرير مرجع لنفسها this.

 

إستدعاء ()initLoader يضمن أن المُحمل مهيأ ونشط. له نتيجتين محتملتين:

 

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

– إذا لم يكن المحمل المحدد بواسطة المعرف موجوداً، تقوم ()initLoader بتشغيل دالة ()LoaderManager.LoaderCallbacks onCreateLoader.

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

 

في كلتا الحالتين، يرتبط تنفيذ LoaderManager.LoaderCallbacks بالمحمل، وسيتم إستدعائه عند تغير حالة المحمل.

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

فسيقوم النظام بإستدعاء ()OnLoadFinished فوراً (أثناء ()initLoader ) ، لذا يجب أن تكون مستعداً لحدوث هذا..

راجع onLoadFinished لمزيد من المناقشة حول هذا الإستدعاء

 

لاحظ أن دالة ()initLoader تقوم بإرجاع المحمل الذي تم إنشاؤه، ولكنك لا تحتاج إلى إلتقاط المرجع إليه. تقوم LoaderManager بإدارة عمر المحمل تلقائياً.

تبدأ LoaderManager وتتوقف عن التحميل عند الضرورة، وتحافظ على حالة المحمل والمحتوى المرتبط به.

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

الأكثر شيوعاً أن تقوم بإستخدام دوال LoaderManager.LoaderCallbacks للتدخل في عملية التحميل عند حدوث أحداث معينة.

لمزيد من المناقشة حول هذا الموضوع، راجع إستخدام إستدعاءات مدير التحميل.

 

 

 

 

 

إعادة تشغيل المحمل

عند إستخدام ()initLoader ، كما هو موضح أعلاه، فإنه يستخدم محمل موجود ذو معرف محدد إن وجد.

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

لتجاهل البيانات القديمة، يمكنك إستخدام ()restartLoader .

مثال، هذا التنفيذ SearchView.OnQueryTextListener يقوم بإعادة تشغيل المحمل عند تغير إستعلام المستخدم.

يحتاج المحمل إلى إعادة التشغيل بحيث يمكنه إستخدام مرشح البحث المنقح لإجراء إستعلام جديد:

KOTLIN

fun onQueryTextChanged(newText: String?): Boolean {
    // Called when the action bar search text has changed.  Update
    // the search filter, and restart the loader to do a new query
    // with this filter.
    mCurFilter = if (newText?.isNotEmpty() == true) newText else null
    supportLoaderManager.restartLoader(0, null, this)
    return true
}

JAVA

public boolean onQueryTextChanged(String newText) {
    // Called when the action bar search text has changed.  Update
    // the search filter, and restart the loader to do a new query
    // with this filter.
    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getSupportLoaderManager().restartLoader(0, null, this);
    return true;
}

 

 

 

 

 

إستخدام إستدعاءات مدير التحميل

LoaderManager.LoaderCallbacks هي واجهة إستدعاء تتيح للعميل التفاعل مع LoaderManager.

المحملات، وخاصة “مؤشر المحمل” CursorLoader، من المتوقع أن تحتفظ ببياناتها بعد إيقافها. يسمح هذا للتطبيقات..

بالإحتفاظ ببياناتها عبر دوال ()onStop و ()onStart للنشاط أو الشظيه، بحيث أنه عندما يعود المستخدمون إلى تطبيق ما..

لن يضطروا إلى إنتظار إعادة تحميل البيانات. يمكنك إستخدام دوال LoaderManager.LoaderCallbacks عند معرفة وقت إنشاء محمل جديد..

ولإخبار التطبيق عندما يحين وقت التوقف عن إستخدام بيانات المحمل.

تتضمن LoaderManager.LoaderCallbacks هذه الدوال:

()onCreateLoader – تنشئ مثيل وتقوم بإرجاع محمل جديد للمعرف المحدد.

()onLoadFinished  – يتم إستدعاؤها عندما ينتهي محمل تم إنشاؤه سابقاً من التحميل.

()onLoaderReset  – يتم إستدعاؤها عند إعادة تعيين محمل تم إنشاؤه سابقاً، مما يجعل بياناته غير متاحة.

يتم وصف هذه الدوال بمزيد من التفاصيل في الأقسام التالية.

 

 

 

onCreateLoader

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

إذا لم يكن موجود، فإنه يقوم بتشغيل دالة ()onCreateLoader  لـ LoaderManager.LoaderCallbacks

هنا حيث تقوم بإنشاء محمل جديد. وعادة ما يكون هذا “مؤشر المحمل” CursorLoader، ولكن يمكنك تنفيذ فئة المحمل الفرعية الخاصة بك.

في هذا المثال، تقوم دالة إستدعاء ()onCreateLoader بإنشاء مؤشر المحمل CursorLoader . يجب إنشاء CursorLoader بإستخدام دالة منشئه..

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

uri – عنوان URI للمحتوى المطلوب إسترجاعه.

projection  – قائمة بالأعمدة التي يجب إرجاعها. سيؤدي تمرير القيمة الفارغة إلى إرجاع جميع الأعمدة غير الفعالة.

selection  إختيار – مرشح يعلن عن الصفوف التي سيتم إرجاعها، تُصاغ على هيئة جملة SQL “أين” (بإستثناء WHERE نفسها).

سيؤدي تمرير القيمة الفارغة إلى إرجاع جميع الصفوف الخاصة بـ URI المعطى.

selectionArgs – يمكنك تضمين s? في التحديد، والذي سيتم إستبداله بقيم من selectionArgs..

بالترتيب الذي تظهر به في التحديد. سيتم ربط القيم كسلسلة.

sortOrder “نوع الفرز”- كيفية ترتيب الصفوف، يصاغ كجملة SQL ORDER BY (بإستثناء ORDER BY نفسه).

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

على سبيل المثال: 

KOTLIN

// If non-null, this is the current filter the user has provided.
private var mCurFilter: String? = null
...
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
    // This is called when a new Loader needs to be created.  This
    // sample only has one Loader, so we don't care about the ID.
    // First, pick the base URI to use depending on whether we are
    // currently filtering.
    val baseUri: Uri = if (mCurFilter != null) {
        Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, Uri.encode(mCurFilter))
    } else {
        ContactsContract.Contacts.CONTENT_URI
    }

    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    val select: String = "((${Contacts.DISPLAY_NAME} NOTNULL) AND (" +
            "${Contacts.HAS_PHONE_NUMBER}=1) AND (" +
            "${Contacts.DISPLAY_NAME} != ''))"
    return (activity as? Context)?.let { context ->
        CursorLoader(
                context,
                baseUri,
                CONTACTS_SUMMARY_PROJECTION,
                select,
                null,
                "${Contacts.DISPLAY_NAME} COLLATE LOCALIZED ASC"
        )
    } ?: throw Exception("Activity cannot be null")
}

JAVA

// If non-null, this is the current filter the user has provided.
String mCurFilter;
...
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // This is called when a new Loader needs to be created.  This
    // sample only has one Loader, so we don't care about the ID.
    // First, pick the base URI to use depending on whether we are
    // currently filtering.
    Uri baseUri;
    if (mCurFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(mCurFilter));
    } else {
        baseUri = Contacts.CONTENT_URI;
    }

    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
            + Contacts.DISPLAY_NAME + " != '' ))";
    return new CursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION, select, null,
            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}

 

 

onLoadFinished

يتم إستدعاء هذه الدالة عند إنتهاء المحمل الذي تم إنشاؤه مسبقاً من التحميل. يتم ضمان إستدعاء هذه الداله..

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

ولكن يجب ألا تقوم بتحرير البيانات بما أن محملها يمتلكها وسوف يهتم بذلك.

سيقوم المحمل بتحرير البيانات بمجرد علمه بأن التطبيق لم يعد يستخدمها. مثال، إذا كانت البيانات عبارة عن مؤشر من CursorLoader ..

يجب عدم إستدعاء ()close عليه بنفسك. إذا تم وضع المؤشر في CursorAdapter، يجب عليك إستخدام دالة ()swapCursor بحيث لا يتم إغلاق المؤشر القديم. مثال:

KOTLIN

private lateinit var mAdapter: SimpleCursorAdapter
...

override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    mAdapter.swapCursor(data)
}

JAVA

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
...

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    mAdapter.swapCursor(data);
}

 

 

onLoaderReset

يتم إستدعاء هذه الدالة عند إعادة تعيين محمل تم إنشاؤه مسبقاً، مما يجعل بياناته غير متاحة.

تتيح لك ميزة الإستدعاء هذه معرفة متى تكون البيانات على وشك الإصدار “التحرير” حتى تتمكن من إزالة مرجعك إليها.

هذا التنفيذ يقوم بإستدعاء ()swapCursor بقيمة فارغة:

KOTLIN

private lateinit var mAdapter: SimpleCursorAdapter
...

override fun onLoaderReset(loader: Loader<Cursor>) {
    // This is called when the last Cursor provided to onLoadFinished()
    // above is about to be closed.  We need to make sure we are no
    // longer using it.
    mAdapter.swapCursor(null)
}

JAVA

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
...

public void onLoaderReset(Loader<Cursor> loader) {
    // This is called when the last Cursor provided to onLoadFinished()
    // above is about to be closed.  We need to make sure we are no
    // longer using it.
    mAdapter.swapCursor(null);
}

 

 

مـثـال

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

ويستخدم CursorLoader لإدارة الإستعلام على الموفر.

بالنسبة للتطبيق للوصول إلى جهات إتصال المستخدم، كما هو موضح في هذا المثال، يجب أن يتضمن ملف الإيضاح الخاص به الإذن “قراءة جهات الإتصال” READ_CONTACTS.

KOTLIN

private val CONTACTS_SUMMARY_PROJECTION: Array<String> = arrayOf(
        Contacts._ID,
        Contacts.DISPLAY_NAME,
        Contacts.CONTACT_STATUS,
        Contacts.CONTACT_PRESENCE,
        Contacts.PHOTO_ID,
        Contacts.LOOKUP_KEY
)


class CursorLoaderListFragment :
        ListFragment(),
        SearchView.OnQueryTextListener,
        LoaderManager.LoaderCallbacks<Cursor> {

    // This is the Adapter being used to display the list's data.
    private lateinit var mAdapter: SimpleCursorAdapter

    // If non-null, this is the current filter the user has provided.
    private var mCurFilter: String? = null

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

        // Give some text to display if there is no data.  In a real
        // application this would come from a resource.
        setEmptyText("No phone numbers")

        // We have a menu item to show in action bar.
        setHasOptionsMenu(true)

        // Create an empty adapter we will use to display the loaded data.
        mAdapter = SimpleCursorAdapter(activity,
                android.R.layout.simple_list_item_2,
                null,
                arrayOf(Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS),
                intArrayOf(android.R.id.text1, android.R.id.text2),
                0
        )
        listAdapter = mAdapter

        // Prepare the loader.  Either re-connect with an existing one,
        // or start a new one.
        loaderManager.initLoader(0, null, this)
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        // Place an action bar item for searching.
        menu.add("Search").apply {
            setIcon(android.R.drawable.ic_menu_search)
            setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
            actionView = SearchView(activity).apply {
                setOnQueryTextListener(this@CursorLoaderListFragment)
            }
        }
    }

    override fun onQueryTextChange(newText: String?): Boolean {
        // Called when the action bar search text has changed.  Update
        // the search filter, and restart the loader to do a new query
        // with this filter.
        mCurFilter = if (newText?.isNotEmpty() == true) newText else null
        loaderManager.restartLoader(0, null, this)
        return true
    }

    override fun onQueryTextSubmit(query: String): Boolean {
        // Don't care about this.
        return true
    }

    override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) {
        // Insert desired behavior here.
        Log.i("FragmentComplexList", "Item clicked: $id")
    }

    override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
        // This is called when a new Loader needs to be created.  This
        // sample only has one Loader, so we don't care about the ID.
        // First, pick the base URI to use depending on whether we are
        // currently filtering.
        val baseUri: Uri = if (mCurFilter != null) {
            Uri.withAppendedPath(Contacts.CONTENT_URI, Uri.encode(mCurFilter))
        } else {
            Contacts.CONTENT_URI
        }

        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        val select: String = "((${Contacts.DISPLAY_NAME} NOTNULL) AND (" +
                "${Contacts.HAS_PHONE_NUMBER}=1) AND (" +
                "${Contacts.DISPLAY_NAME} != ''))"
        return (activity as? Context)?.let { context ->
            CursorLoader(
                    context,
                    baseUri,
                    CONTACTS_SUMMARY_PROJECTION,
                    select,
                    null,
                    "${Contacts.DISPLAY_NAME} COLLATE LOCALIZED ASC"
            )
        } ?: throw Exception("Activity cannot be null")
    }

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
        // Swap the new cursor in.  (The framework will take care of closing the
        // old cursor once we return.)
        mAdapter.swapCursor(data)
    }

    override fun onLoaderReset(loader: Loader<Cursor>) {
        // This is called when the last Cursor provided to onLoadFinished()
        // above is about to be closed.  We need to make sure we are no
        // longer using it.
        mAdapter.swapCursor(null)
    }
}

JAVA

public static class CursorLoaderListFragment extends ListFragment
        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {

    // This is the Adapter being used to display the list's data.
    SimpleCursorAdapter mAdapter;

    // If non-null, this is the current filter the user has provided.
    String mCurFilter;

    @Override public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // Give some text to display if there is no data.  In a real
        // application this would come from a resource.
        setEmptyText("No phone numbers");

        // We have a menu item to show in action bar.
        setHasOptionsMenu(true);

        // Create an empty adapter we will use to display the loaded data.
        mAdapter = new SimpleCursorAdapter(getActivity(),
                android.R.layout.simple_list_item_2, null,
                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
        setListAdapter(mAdapter);

        // Prepare the loader.  Either re-connect with an existing one,
        // or start a new one.
        getLoaderManager().initLoader(0, null, this);
    }

    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // Place an action bar item for searching.
        MenuItem item = menu.add("Search");
        item.setIcon(android.R.drawable.ic_menu_search);
        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        SearchView sv = new SearchView(getActivity());
        sv.setOnQueryTextListener(this);
        item.setActionView(sv);
    }

    public boolean onQueryTextChange(String newText) {
        // Called when the action bar search text has changed.  Update
        // the search filter, and restart the loader to do a new query
        // with this filter.
        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
        getLoaderManager().restartLoader(0, null, this);
        return true;
    }

    @Override public boolean onQueryTextSubmit(String query) {
        // Don't care about this.
        return true;
    }

    @Override public void onListItemClick(ListView l, View v, int position, long id) {
        // Insert desired behavior here.
        Log.i("FragmentComplexList", "Item clicked: " + id);
    }

    // These are the Contacts rows that we will retrieve.
    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
        Contacts._ID,
        Contacts.DISPLAY_NAME,
        Contacts.CONTACT_STATUS,
        Contacts.CONTACT_PRESENCE,
        Contacts.PHOTO_ID,
        Contacts.LOOKUP_KEY,
    };
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // This is called when a new Loader needs to be created.  This
        // sample only has one Loader, so we don't care about the ID.
        // First, pick the base URI to use depending on whether we are
        // currently filtering.
        Uri baseUri;
        if (mCurFilter != null) {
            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                    Uri.encode(mCurFilter));
        } else {
            baseUri = Contacts.CONTENT_URI;
        }

        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
                + Contacts.DISPLAY_NAME + " != '' ))";
        return new CursorLoader(getActivity(), baseUri,
                CONTACTS_SUMMARY_PROJECTION, select, null,
                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // Swap the new cursor in.  (The framework will take care of closing the
        // old cursor once we return.)
        mAdapter.swapCursor(data);
    }

    public void onLoaderReset(Loader<Cursor> loader) {
        // This is called when the last Cursor provided to onLoadFinished()
        // above is about to be closed.  We need to make sure we are no
        // longer using it.
        mAdapter.swapCursor(null);
    }
}

 

 

المزيد من الأمثلة

توضح الأمثلة التالية كيفية إستخدام المحمل:

LoaderCursor – نسخة كاملة من المقتطف الموضح أعلاه.

إسترداد قائمة جهات الإتصال – عبارة عن عرض توضيحي يستخدم CursorLoader لإسترداد البيانات من موفر جهات الإتصال.

LoaderThrottle – مثال على كيفية إستخدام الصمام لتقليل عدد الإستعلامات التي يقوم بها موفر المحتوى عند تغيير بياناته.

AsyncTaskLoader – مثال يستخدم AsyncTaskLoader لتحميل التطبيقات المثبتة حالياً من مدير الحزم.

 


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

الإعلانات