عامل التحميل

تم إيقاف برامج التحميل اعتبارًا من الإصدار 9 من نظام Android (المستوى 28 من واجهة برمجة التطبيقات). الخيار الموصى به عند التعامل مع بيانات التحميل أثناء دورتَي النشاط Activity وFragment، يجب استخدام مجموعة من عناصر ViewModel وLiveData عرض النماذج التي تعاني من تغييرات التهيئة، مثل الرافعات، ولكن مع رموز معيارية أقل. يوفّر LiveData طريقة لتحميل البيانات التي يمكنك إعادة استخدامها مع مراعاة مراحل النشاط. نماذج عرض متعددة. يمكنك أيضًا دمج LiveData باستخدام MediatorLiveData وأي طلبات بحث يمكن ملاحظتها، مثل تلك الواردة من قاعدة بيانات الغرفة، يمكن استخدامها لرصد التغييرات للبيانات.

يتوفّر ViewModel وLiveData أيضًا في الحالات التي لا يمكنك فيها الوصول إلى الخدمة. إلى LoaderManager، كما هو الحال في Service يتيح لك استخدام الاثنين في طريقة سهلة للوصول إلى البيانات التي يحتاجها تطبيقك دون الحاجة إلى التعامل مع واجهة المستخدم دورة حياة المنتج. لمزيد من المعلومات عن "LiveData"، يُرجى الاطّلاع على نظرة عامة على "LiveData" لمزيد من المعلومات حول ViewModel، يمكنك الاطّلاع على نظرة عامة على ViewModel.

تتيح لك Loader API تحميل البيانات من أداة توفير المحتوى أو مصدر بيانات آخر للعرض في FragmentActivity أو Fragment.

وبدون برامج التحميل، تتضمن بعض المشكلات التي قد تواجهها ما يلي:

  • إذا استرجعت البيانات مباشرةً في النشاط أو الجزء، سيعني المستخدمون تعاني من نقص في سرعة الاستجابة بسبب الأداء البطيء المحتمل من مؤشر ترابط واجهة المستخدم.
  • إذا جلبت البيانات من سلسلة محادثات أخرى، ربما باستخدام AsyncTask، فأنت مسؤول عن إدارة سلسلة المحادثات هذه وسلسلة واجهة المستخدم من خلال أنشطة متنوعة أو أحداث مراحل نشاط مجزأة، مثل onDestroy() وتغييرات الإعدادات.

تحل المحمّلات هذه المشاكل وتتضمن مزايا أخرى:

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

ملخّص Loader API

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

الصف/الواجهة الوصف
LoaderManager تمثّل هذه السمة فئة مجردة مرتبطة بسمة FragmentActivity أو Fragment لإدارة واحد أو أكثر Loader مثال لا يوجد سوى LoaderManager لكل نشاط أو جزء، ولكن بإمكان LoaderManager إدارة برامج تحميل متعددة.

للحصول على LoaderManager، اتصل بالرقم getSupportLoaderManager() من النشاط أو الجزء.

لبدء تحميل البيانات من أداة تحميل، يمكنك استدعاء أحد الإجراءين initLoader() أو restartLoader() يحدد النظام تلقائيًا ما إذا كانت هناك أداة تحميل تحمل رقم تعريف العدد الصحيح نفسه أم لا وإنشاء أداة تحميل جديدة أو إعادة استخدام أداة تحميل حالية.

LoaderManager.LoaderCallbacks تحتوي هذه الواجهة على طرق معاودة الاتصال التي يتم استدعاؤها عند أحداث القائم بالتحميل. تحدِّد الواجهة ثلاث طرق لمعاودة الاتصال:
  • onCreateLoader(int, Bundle): يتم استدعاؤه عندما يحتاج النظام إلى إنشاء أداة تحميل جديدة. في التعليمات البرمجية، إنشاء كائن Loader وإرجاعه إلى النظام.
  • onLoadFinished(Loader<D>, D): يتم استدعاؤه عندما ينتهي برنامج التحميل من تحميل البيانات. أنت عادةً عرض البيانات للمستخدم في التعليمات البرمجية.
  • onLoaderReset(Loader<D>): يتم استدعاؤه عند إعادة تعيين برنامج تحميل تم إنشاؤه مسبقًا، عندما تقوم بالاتصال destroyLoader(int) أو وقت حدوث النشاط إتلاف أو إتلاف جزء، مما يجعل بياناته غير متوفرة. في التعليمات البرمجية، وإزالة أي مراجع لبيانات القائم بالتحميل.
وينفذ النشاط أو الجزء عادةً هذه الواجهة، التسجيل عند الاتصال initLoader() أو restartLoader()
Loader وتنفّذ المحمّلات تحميل البيانات. هذه الفئة مجردة وتخدم باعتبارها الفئة الأساسية لجميع القائمات بالتحميل. يمكنك إنشاء فئة فرعية Loader أو استخدام أيّ من الميزات المضمَّنة التالية لتبسيط التنفيذ:

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

استخدام برامج التحميل في أحد التطبيقات

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

بدء التحميل

يدير LoaderManager حدثًا واحدًا أو أكثر من أحداث Loader ضمن FragmentActivity أو Fragment ويتم توفير سمة LoaderManager واحدة فقط لكل نشاط أو جزء.

أنت عادةً يجب إعداد Loader ضمن طريقة onCreate() للنشاط أو دالة الجزء طريقة onCreate() إِنْتَ قم بذلك على النحو التالي:

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.
  • الوسيطات الاختيارية التي سيتم توفيرها لبرنامج التحميل على البناء (null في هذا المثال).
  • عملية تنفيذ LoaderManager.LoaderCallbacks التي طلبات LoaderManager للإبلاغ عن أحداث أداة التحميل. في هذه الدورة، سبيل المثال، تنفّذ الفئة المحلية واجهة LoaderManager.LoaderCallbacks، لذا فإنها تمرر مرجعًا إلى نفسه، this.

يضمن طلب initLoader() أن يقوم برنامج التحميل نشط ونشط. له نتيجتان محتملتان:

  • إذا كانت أداة التحميل التي تم تحديدها بواسطة رقم التعريف موجودة من قبل، يتم استخدام آخر برنامج تحميل تم إنشاؤه من جديد.
  • إذا لم تكن أداة التحميل التي حدّدها المعرّف متوفرة، يؤدي initLoader() إلى تشغيل طريقة LoaderManager.LoaderCallbacks onCreateLoader() هذا هو المكان الذي تنفذ فيه التعليمة البرمجية لإنشاء مثيل وعرض أداة تحميل جديدة. لمزيد من النقاشات، راجِع القسم حول onCreateLoader.

في كلتا الحالتين، قيمة السمة LoaderManager.LoaderCallbacks المحددة يرتبط تنفيذ هذا الإجراء بالقائم بالتحميل، ويتم استدعاؤه عندما تغييرات حالة برنامج التحميل. إذا كان المتصل في نقطة هذه المكالمة برنامج التحميل المطلوب وبرنامج التحميل المطلوب وقد تم إنشاؤه البيانات، فسيتصل النظام بـ onLoadFinished() مباشرةً خلال initLoader(). يجب أن تكون مستعدًا لذلك. لمزيد من المناقشة حول معاودة الاتصال هذه، راجع القسم حول onLoadFinished

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

يعني ذلك أنّك نادرًا ما تتفاعل مع برامج التحميل مباشرةً. تستخدم طرق LoaderManager.LoaderCallbacks الأكثر شيوعًا للتدخل في عملية التحميل. العملية عند وقوع أحداث معينة. لمزيد من المناقشة لهذا الموضوع، يمكنك الاطّلاع على القسم استخدام استدعاءات LoaderManager.

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

عند استخدام 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.
    curFilter = 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.
    curFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getSupportLoaderManager().restartLoader(0, null, this);
    return true;
}

استخدام استدعاءات LoaderManager

LoaderManager.LoaderCallbacks هي واجهة لمعاودة الاتصال تُتيح للعميل التفاعل مع LoaderManager.

من المتوقّع أن تنفّذ عمليات التحميل، لا سيّما CursorLoader، الاحتفاظ ببياناتها بعد إيقافها. يسمح هذا للتطبيقات بالاحتفاظ بالبيانات عبر طريقتَي onStop() وonStart() الخاصتَين بالنشاط أو الجزء، وذلك لكي عند عودة المستخدمين إلى أحد التطبيقات، لن يضطروا إلى انتظار البيانات إعادة التحميل.

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

تتضمن LoaderManager.LoaderCallbacks هذه الطرق:

  • onCreateLoader(): ينشئ مثيلاً ويعرض قيمة Loader جديدة للمعرّف المحدّد.
  • onLoadFinished(): يتم استدعاؤه عندما ينتهي تحميل برنامج تحميل تم إنشاؤه مسبقًا.
  • onLoaderReset(): عند إعادة تعيين برنامج تحميل تم إنشاؤه مسبقًا، ومن ثم يصبح البيانات غير متوفرة.

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

OnCreateLoader

عندما تحاول الوصول إلى أداة تحميل، من خلال initLoader() مثلاً، تتحقق الأداة لمعرفة ما إذا توجد أداة التحميل المحددة بواسطة المعرف. وفي حال عدم حدوث ذلك، سيتم تشغيل طريقة LoaderManager.LoaderCallbacks onCreateLoader(). هذا النمط هي المكان الذي تقوم فيه بإنشاء تحميل جديد. تكون هذه الفئة عادةً CursorLoader، ولكن يمكنك تنفيذ فئتك الفرعية Loader.

في المثال التالي، تشير السمة onCreateLoader() تنشئ طريقة استدعاء الدالة CursorLoader باستخدام طريقة الدالة الإنشائية، والتي مجموعة كاملة من المعلومات اللازمة لإجراء طلب بحث في ContentProvider. وعلى وجه التحديد، يحتاج المتصفّح إلى ما يلي:

  • uri: معرّف الموارد المنتظم (URI) للمحتوى المطلوب استرداده.
  • projection: قائمة بالأعمدة المطلوب عرضها. تمرير الكرة تعرض null جميع الأعمدة، وهي غير فعّالة.
  • الاختيار: فلتر يحدِّد الصفوف المطلوب عرضها تنسيق كعبارة SQL WHERE (باستثناء WHERE نفسه). تمرير الكرة تعرض null جميع الصفوف لمعرّف الموارد المنتظم (URI) المحدّد.
  • selectionArgs: إذا أدرجت ?s في التحديد، سيتم يتم استبدالها بالقيم من selectionArgs بالترتيب الذي تظهر فيه التحديد. يتم ربط القيم كسلاسل.
  • sortOrder: كيفية ترتيب الصفوف، المنسقة كـ SQL عبارة ORDER BY (باستثناء ORDER BY نفسها). جارٍ اجتياز الاختبار: null تستخدم نظام الفرز الافتراضي، الذي قد يكون غير مرتب.

Kotlin

// If non-null, this is the current filter the user has provided.
private var curFilter: 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 (curFilter != null) {
        Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, Uri.encode(curFilter))
    } 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 curFilter;
...
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 (curFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(curFilter));
    } 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");
}

تم الانتهاء من التحميل

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

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

Kotlin

private lateinit var adapter: 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.)
    adapter.swapCursor(data)
}

Java

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter adapter;
...
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.)
    adapter.swapCursor(data);
}

عند إعادة تعيين

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

يتطلّب هذا التنفيذ swapCursor() بقيمة null:

Kotlin

private lateinit var adapter: 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.
    adapter.swapCursor(null)
}

Java

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter adapter;
...
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.
    adapter.swapCursor(null);
}

مثال

على سبيل المثال، في ما يلي التنفيذ الكامل للسمة Fragment التي تعرض 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 curFilter: String? = null

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

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, 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
    }

    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.
        curFilter = 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 (curFilter != null) {
            Uri.withAppendedPath(Contacts.CONTENT_URI, Uri.encode(curFilter))
        } 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 curFilter;

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

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

    @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, 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);
    }

    @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.
        curFilter = !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 (curFilter != null) {
            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                    Uri.encode(curFilter));
        } 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: مثال على كيفية استخدام ميزة التقييد لتقليل عدد من طلبات البحث التي يُجريها موفر المحتوى عندما تتغير بياناته.