מטענים

מטענים הוצאו משימוש החל מ-Android 9 (רמת API 28). האפשרות המומלצת עבור בנושא טעינת נתונים במהלך טיפול במחזורי החיים של Activity ו-Fragment, שילוב של ViewModel אובייקטים ו-LiveData. הצגת המודלים לשרוד שינויים בתצורה, כמו מטענים, אבל עם פחות קוד סטנדרטי. LiveData מספק דרך לטעינת נתונים בהתאם למחזור החיים שניתן לעשות בהם שימוש חוזר מודלים מרובים של תצוגה. אפשר גם לשלב את LiveData באמצעות MediatorLiveData. כל השאילתות שניתנות למדידה, כמו שאילתה מסד הנתונים של Room – אפשר להשתמש בו כדי להבחין בשינויים לנתונים.

ViewModel ו-LiveData זמינים גם במצבים שבהם אין לך גישה אל LoaderManager, למשל Service. שימוש בשני הסוגים tandem מספקת דרך קלה לגשת לנתונים שהאפליקציה שלך צריכה ללא צורך בטיפול בממשק המשתמש במחזור החיים. מידע נוסף על LiveData זמין ב סקירה כללית של LiveData. מידע נוסף על ViewModel, אפשר לעיין בסקירה הכללית של ViewModel.

ה-Loader API מאפשר לטעון נתונים ספק תוכן או מקור נתונים אחר להצגה בFragmentActivity או Fragment.

ללא מטענים, יכול להיות שתיתקלו בבעיות הבאות:

  • אם אתם מאחזרים את הנתונים ישירות בפעילות או במקטע, המשתמשים שלכם סובלים מחוסר תגובה בגלל שהביצועים עשויים להיות איטיים מהשרשור של ממשק המשתמש.
  • אם מאחזרים את הנתונים משרשור אחר, אולי באמצעות AsyncTask, אז אתם אחראים לנהל גם את השרשורים האלה והשרשור בממשק המשתמש, שעובר דרך אירועים שונים של פעילות או מחזור חיים של מקטעים, כמו onDestroy() ושינויים בהגדרות האישיות.

מטענים פותרים את הבעיות האלה וכוללים יתרונות נוספים:

  • מטענים פועלים בשרשורים נפרדים כדי למנוע ממשק משתמש איטי או לא מגיב.
  • טעינות הנתונים מפשטות את ניהול השרשורים באמצעות שיטות קריאה חוזרת (callback) כשאירועים יתרחשו.
  • המטענים נשארים ושומרים את התוצאות במטמון בכל שינויי ההגדרות כדי למנוע של שאילתות כפולות.
  • מטענים יכולים להטמיע צופה כדי לנטר את השינויים מקור הנתונים. לדוגמה, CursorLoader באופן אוטומטי רושם ContentObserver כדי להפעיל טעינה מחדש כשהנתונים משתנים.

סיכום של Loader API

יש מספר מחלקות וממשקים שעשויים להיות מעורבים בשימוש טעינה באפליקציה. הסיכום שלהם מוצג בטבלה הבאה:

כיתה/ממשק תיאור
LoaderManager כיתה מופשטת שמשויכת לFragmentActivity או Fragment לניהול חשבון אחד או יותר Loader מופעים. יש רק אחד LoaderManager לכל פעילות או מקטע, אבל LoaderManager יכול לנהל מטענים מרובים.

כדי לקבל LoaderManager, צריך להתקשר למספר getSupportLoaderManager() מהפעילות או המקטע.

כדי להתחיל לטעון נתונים מטוען, צריך לבצע קריאה לאחד initLoader() או restartLoader(). המערכת קובעת באופן אוטומטי אם כבר קיים רכיב טעינה עם אותו מספר שלם קיים ויוצר טוען חדש או עושה שימוש חוזר ב-Loader קיים.

LoaderManager.LoaderCallbacks הממשק הזה מכיל שיטות לקריאה חוזרת (callback) שנקראות כאשר ואירועי Loader מתרחשים. בממשק מוגדרות שלוש שיטות לקריאה חוזרת (callback):
  • onCreateLoader(int, Bundle): שנקראה כשהמערכת צריכה ליצור טוען חדש. בקוד שלכם, יוצרים אובייקט Loader ומחזירים אותו אל במערכת.
  • onLoadFinished(Loader<D>, D): שנקראה כאשר ה-Loader מסיים לטעון את הנתונים. בדרך כלל תציג את הנתונים למשתמש בקוד שלך.
  • onLoaderReset(Loader<D>): נשלחת קריאה כשמתבצע איפוס של רכיב טעינה שנוצר בעבר, כשקוראים destroyLoader(int) או מתי הפעילות או מקטע מושמדים כך שהנתונים שלהם לא זמינים. בקוד שלכם, להסיר כל הפניה לנתוני הטוען.
בדרך כלל, הפעילות או המקטע שלך מיישמים את הממשק הזה, נרשם כשמתקשרים initLoader() או restartLoader().
Loader טוענים מבצעים את טעינת הנתונים. הכיתה הזו מופשטת ומוצגת כסיווג הבסיס לכל המטענים. אפשר לבצע תת-מחלקה ישירות Loader או להשתמש באחד מהדפדפנים המובְנים הבאים מחלקות משנה כדי לפשט את ההטמעה:

בקטעים הבאים מוסבר איך להשתמש מחלקות וממשקים באפליקציה.

שימוש במטענים באפליקציה

הקטע הזה מתאר איך להשתמש במטענים באפליקציה ל-Android. אפליקציה שמשתמשת במטענים בדרך כלל כוללת את הפרטים הבאים:

הפעלת כלי טעינה

השדה LoaderManager מנהל מופע אחד או יותר של Loader בתוך FragmentActivity או Fragment יש רק LoaderManager אחד לכל פעילות או מקטע.

בדרך כלל לאתחל Loader בתוך ה-method של 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);

ל-method initLoader() נדרש את הפרמטרים הבאים:

  • מזהה ייחודי שמזהה את הטוענים. בדוגמה הזו, המזהה הוא 0.
  • ארגומנטים אופציונליים שצריך לספק למטען ב- עבודות בדרך (null בדוגמה הזו).
  • הטמעת LoaderManager.LoaderCallbacks, הקריאות של LoaderManager לדיווח על אירועי טעינה. כאן לדוגמה, המחלקה המקומית מממשת את הממשק של LoaderManager.LoaderCallbacks ולכן היא מעבירה הפניה לעצמו, this.

הקריאה initLoader() מבטיחה שהטעינה מאותחל ופעיל. יש לכך שתי תוצאות אפשריות:

  • אם הטוען שצוין במזהה כבר קיים, הטוען האחרון שנוצר נעשה שימוש חוזר.
  • אם הטוען שצוין במזהה לא קיים, הפקודה initLoader() מפעילה את אמצעי התשלום LoaderManager.LoaderCallbacks onCreateLoader(). כאן מיישמים את הקוד כדי ליצור מופע ולהחזיר Loader חדש. לדיון נוסף, עיינו בקטע על onCreateLoader.

בכל מקרה, הערך הנתון של LoaderManager.LoaderCallbacks משויך לטוען והוא מופעל כאשר שינויים במצב ה-Loader. אם, בנקודת השיחה הזו, המתקשר נמצא והטוען המבוקש כבר קיים ויצר ואז המערכת קוראת ל-onLoadFinished() מיד, במהלך initLoader(). אתם צריכים להיות מוכנים לכך. לדיון נוסף על הקריאה החוזרת הזו, אפשר לעיין בקטע בנושא onLoadFinished

ה-method initLoader() מחזירה את הערך Loader שנוצר, אבל אין צורך לתעד אליו הפניה. LoaderManager מנהלת באופן אוטומטי. LoaderManager הטעינה מתחילה ומפסיקה במקרה הצורך ושומרת על מצב הטעינה ואת התוכן המשויך אליו.

כלומר, לעיתים נדירות אין לך אינטראקציה עם מטענים ישירות. בדרך כלל משתמשים בשיטות LoaderManager.LoaderCallbacks כדי להתערב בטעינה כאשר מתרחשים אירועים מסוימים. לדיון נוסף בנושא הזה, ראו שימוש בקריאות חוזרות (callbacks) של 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;
}

שימוש בקריאות חוזרות (callbacks) של LoaderManager

LoaderManager.LoaderCallbacks הוא ממשק קריאה חוזרת שמאפשרת ללקוח ליצור אינטראקציה עם LoaderManager.

אדמינים, במיוחד CursorLoader, צפויים ישמרו את הנתונים שלהם אחרי שיופסקו. כך האפליקציות יכולות לשמור נתונים בכל ה-methods onStop() ו-onStart() של הפעילות או המקטע, כך כשמשתמשים חוזרים לאפליקציה, הם לא צריכים להמתין לנתונים לטעון מחדש.

משתמשים בשיטות LoaderManager.LoaderCallbacks כדי לדעת מתי ליצור טוען חדש ולציין לאפליקציה מתי כדי להפסיק להשתמש בנתוני Loader.

LoaderManager.LoaderCallbacks כולל אלה אמצעי תשלום:

  • onLoadFinished(): שנקראה כאשר טוען שנוצר בעבר סיים את הטעינה.
  • onLoaderReset(): שנקראה כשטוען שנוצר בעבר מתאפס, וכך הופך הנתונים אינם זמינים.

השיטות האלה מתוארות בפירוט בקטעים הבאים.

onCreateLoader

כשמנסים לגשת לרכיב טעינה, למשל דרך initLoader(), המערכת בודקת אם רכיב הטעינה שצוין על ידי המזהה קיים. אם היא לא מתחילה כך, היא מפעילה את השיטה LoaderManager.LoaderCallbacks onCreateLoader(). הזה הוא המקום שבו יוצרים טוען חדש. בדרך כלל זהו מחלקה של CursorLoader, אבל אתם יכולים להטמיע מחלקה משנית משלכם ב-Loader.

בדוגמה הבאה, הפרמטר onCreateLoader() שיטת הקריאה החוזרת יוצרת CursorLoader באמצעות שיטת ה-constructor שלה, מחייב את כל המידע הנדרש לביצוע שאילתה ב-ContentProvider. ספציפית, הוא זקוק לדברים הבאים:

  • uri: ה-URI של התוכן שצריך לאחזר.
  • projection: רשימה של העמודות שצריך להחזיר. מסירות הפונקציה null מחזירה את כל העמודות, והיא לא יעילה.
  • selection (בחירה): מסנן שקובע אילו שורות להחזיר בפורמט 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");
}

onLoadסיימתי

מתבצעת קריאה לשיטה הזו כאשר טעינה שנוצרה בעבר מסיימת את הטעינה. מובטחת קריאה לשיטה הזו לפני הפצת הנתונים האחרונים שסופק לטעינת הנתונים הזו. בשלב הזה, הסירו את כל השימוש ב- את הנתונים הישנים, כי הם יושקו. עם זאת, אל תפרסמו את הנתונים. בעצמך - הטוען הוא הבעלים שלו ומטפל בזה.

הטוען משחרר את הנתונים לאחר שהוא יודע שהאפליקציה כבר לא באמצעותו. לדוגמה, אם הנתונים הם סמן מ-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);
}

onLoaderreset

לשיטה הזו קוראים כשמתבצע איפוס של טוען שנוצר בעבר, והנתונים שבו לא זמינים. הקריאה החוזרת מאפשרת לכם לגלות מתי הנתונים ישוחרר כדי שתוכלו להסיר את ההפניה אליו.

ההטמעה הזאת קוראת 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: דוגמה לאופן שבו ניתן להשתמש בויסות נתונים כדי לצמצם את המספר של שאילתות שספק תוכן מבצע כאשר הנתונים שלו משתנים.