ตัวโหลด

ตัวโหลดเลิกใช้งานไปแล้วตั้งแต่ Android 9 (API ระดับ 28) ตัวเลือกที่แนะนำสำหรับ ในการจัดการกับการโหลดข้อมูลขณะจัดการวงจรการใช้งาน Activity และ Fragment คือการใช้ ชุดค่าผสมของออบเจ็กต์ ViewModel รายการ และ LiveData ดูโมเดลที่ไม่ต้องเปลี่ยนแปลงการกำหนดค่า เช่น ตัวโหลด แต่ โค้ด Boilerplate น้อยกว่า LiveData มอบวิธีโหลดข้อมูลแบบรับรู้วงจรซึ่งคุณนำมาใช้ซ้ำได้ รูปแบบมุมมองที่หลากหลาย นอกจากนี้ คุณยังสามารถรวม LiveData โดยใช้ MediatorLiveData ข้อความค้นหาที่สังเกตได้ เช่น คำค้นหาจาก ฐานข้อมูลห้อง ใช้สำหรับสังเกตการเปลี่ยนแปลง กับข้อมูล

คุณจะยังใช้ ViewModel และ LiveData ได้ในกรณีที่คุณไม่มีสิทธิ์เข้าถึง ไปยัง LoaderManager เช่น Service การใช้มิติข้อมูล 2 อย่างใน ส่วนเชื่อมต่อช่วยให้คุณเข้าถึงข้อมูลที่แอปของคุณต้องการได้ง่ายๆ โดยไม่ต้องจัดการ UI ใหม่ ดูข้อมูลเพิ่มเติมเกี่ยวกับ LiveData ได้ที่ ภาพรวม LiveData หากต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับ ViewModel โปรดดูภาพรวมของ ViewModel

Loader API ช่วยให้คุณโหลดข้อมูลจาก ผู้ให้บริการเนื้อหา หรือแหล่งข้อมูลอื่นๆ เพื่อแสดงใน FragmentActivity หรือ Fragment

หากไม่มีตัวโหลด ปัญหาบางประการที่คุณอาจพบมีดังนี้:

  • หากคุณดึงข้อมูลในกิจกรรมหรือส่วนย่อยโดยตรง ผู้ใช้ของคุณ ขาดการตอบสนองเนื่องจากการทำงานอาจช้า จากชุดข้อความ UI
  • หากคุณดึงข้อมูลจากชุดข้อความอื่น อาจเป็นเพราะ AsyncTask คุณก็มีหน้าที่จัดการชุดข้อความนั้น และเธรด UI ผ่านกิจกรรมหรือเหตุการณ์ในวงจรของแฟรกเมนต์ต่างๆ เช่น onDestroy()และการเปลี่ยนแปลงการกำหนดค่า

ตัวโหลดช่วยแก้ปัญหาเหล่านี้และมีประโยชน์อื่นๆ ดังนี้

  • ตัวโหลดจะทำงานในชุดข้อความแยกกันเพื่อป้องกัน UI ที่ช้าหรือไม่ตอบสนอง
  • ตัวโหลดลดความซับซ้อนในการจัดการชุดข้อความด้วยเมธอด Callback เมื่อเหตุการณ์ เกิดขึ้น
  • ตัวโหลดจะคงอยู่และแคชผลลัพธ์ของการเปลี่ยนแปลงการกำหนดค่าเพื่อป้องกัน ข้อความค้นหาที่ซ้ำกัน
  • ตัวโหลดสามารถใช้ผู้สังเกตการณ์เพื่อติดตามดูการเปลี่ยนแปลงของ แหล่งข้อมูล เช่น CursorLoader โดยอัตโนมัติ ลงทะเบียน ContentObserver เพื่อทริกเกอร์การโหลดซ้ำ เมื่อข้อมูลเปลี่ยนแปลง

สรุป API ของ Loader

มีคลาสและอินเทอร์เฟซหลายรายการที่อาจเกี่ยวข้องเมื่อใช้ ตัวโหลดในแอป ซึ่งมีสรุปไว้ในตารางต่อไปนี้

คลาส/อินเทอร์เฟซ คำอธิบาย
LoaderManager คลาส Abstract ที่เชื่อมโยงกับ FragmentActivity หรือ Fragment สำหรับการจัดการอย่างน้อย 1 รายการ Loader อินสแตนซ์ มีเพียงรายการเดียวเท่านั้น LoaderManager ต่อกิจกรรมหรือส่วนย่อย แต่ LoaderManager จัดการตัวโหลดได้หลายรายการ

หากต้องการ LoaderManager ให้โทรหา getSupportLoaderManager() จากกิจกรรมหรือส่วนย่อย

หากต้องการเริ่มโหลดข้อมูลจากตัวโหลด ให้เรียกใช้ initLoader() หรือ restartLoader() ระบบจะพิจารณาโดยอัตโนมัติว่าตัวโหลดที่มีรหัสจำนวนเต็มเดียวกันหรือไม่ มีอยู่และสร้างตัวโหลดใหม่ หรือนำตัวโหลดที่มีอยู่มาใช้ซ้ำ

LoaderManager.LoaderCallbacks อินเทอร์เฟซนี้มีเมธอด Callback ซึ่งถูกเรียกเมื่อ มีเหตุการณ์ของตัวโหลดเกิดขึ้น อินเทอร์เฟซจะกำหนดวิธีการติดต่อกลับ 3 วิธี ดังนี้
  • onCreateLoader(int, Bundle): เมื่อระบบจำเป็นต้องสร้างตัวโหลดใหม่ ในโค้ดของคุณ สร้างออบเจ็กต์ Loader และส่งไปยัง ระบบ
  • onLoadFinished(Loader<D>, D): ถูกเรียกเมื่อตัวโหลดโหลดข้อมูลเสร็จแล้ว ปกติแล้วคุณ แสดงข้อมูลต่อผู้ใช้ในโค้ดของคุณ
  • onLoaderReset(Loader<D>): เรียกใช้เมื่อมีการรีเซ็ตตัวโหลดที่สร้างขึ้นก่อนหน้านี้ เมื่อคุณเรียกใช้ destroyLoader(int) หรือเมื่อกิจกรรม หรือส่วนย่อยถูกทำลาย ทำให้ไม่มีข้อมูล ในโค้ดของคุณ นำการอ้างอิงไปยังข้อมูลของตัวโหลดออก
ตามปกติกิจกรรมหรือส่วนย่อยของคุณจะปรับใช้อินเทอร์เฟซนี้ และ ที่ลงทะเบียนเมื่อคุณโทร initLoader()หรือ restartLoader()
Loader ตัวโหลดดำเนินการโหลดข้อมูล ชั้นเรียนนี้เป็นนามธรรมและให้บริการ เป็นคลาสพื้นฐานสำหรับตัวโหลดทั้งหมด คุณสามารถในชั้นเรียนย่อยได้โดยตรง Loader หรือใช้หนึ่งในระบบต่อไปนี้ คลาสย่อยต่างๆ เพื่อให้การใช้งานง่ายขึ้น
  • AsyncTaskLoader: ตัวโหลดเชิงนามธรรมที่ จะระบุ AsyncTask เพื่อดำเนินการโหลดบน ชุดข้อความ
  • CursorLoader: คลาสย่อยที่เป็นรูปธรรมของ AsyncTaskLoader สำหรับข้อมูลการโหลดที่ไม่พร้อมกัน จาก ContentProvider ค้นหา ContentResolver และแสดงผล Cursor

ส่วนต่อไปนี้จะแสดงวิธีใช้ คลาสและอินเทอร์เฟซในแอปพลิเคชัน

ใช้ตัวโหลดในแอปพลิเคชัน

หัวข้อนี้จะอธิบายวิธีใช้ตัวโหลดในแอปพลิเคชัน Android CANNOT TRANSLATE แอปพลิเคชันที่ใช้ตัวโหลดมักจะประกอบด้วยสิ่งต่อไปนี้:

  • FragmentActivity หรือ Fragment
  • อินสแตนซ์ของ LoaderManager
  • CursorLoader สำหรับโหลดข้อมูลที่สนับสนุนโดย ContentProvider หรือคุณจะใช้คลาสย่อยของตัวเองก็ได้ จาก Loader หรือ AsyncTaskLoader ถึง โหลดข้อมูลจากแหล่งที่มาอื่น
  • การใช้งานสำหรับ LoaderManager.LoaderCallbacks นี่คือที่ที่คุณสร้างตัวโหลดใหม่และจัดการการอ้างอิงของคุณที่มีอยู่ ตัวโหลด
  • วิธีแสดงข้อมูลของตัวโหลด เช่น SimpleCursorAdapter
  • แหล่งข้อมูล เช่น ContentProvider เมื่อใช้ CursorLoader

เริ่มตัวโหลด

LoaderManager จัดการอินสแตนซ์ Loader อย่างน้อย 1 รายการภายใน FragmentActivity หรือ Fragment แต่ละกิจกรรมหรือส่วนย่อยจะมี LoaderManager ได้เพียง 1 รายการเท่านั้น

ปกติแล้วคุณ เริ่มต้น 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() จะทำให้มั่นใจได้ว่าตัวโหลด ได้รับการเริ่มต้นและใช้งาน โดยมีผลลัพธ์ที่เป็นไปได้ 2 อย่างดังนี้

  • หากมีตัวโหลดที่ระบุโดยรหัสอยู่แล้ว ตัวโหลดที่สร้างล่าสุด ซึ่งก็คือการใช้ซ้ำ
  • หากตัวโหลดที่ระบุโดยรหัสไม่มีอยู่ initLoader() ทริกเกอร์การเรียก LoaderManager.LoaderCallbacks เมธอด onCreateLoader() นี่คือที่ที่คุณจะติดตั้งโค้ดเพื่อสร้างอินสแตนซ์และส่งคืนตัวโหลดใหม่ สำหรับการสนทนาเพิ่มเติม โปรดดูที่ส่วนเกี่ยวกับ onCreateLoader

ไม่ว่าจะเป็นกรณีใด LoaderManager.LoaderCallbacks ที่ระบุ มีความเกี่ยวข้องกับตัวโหลด และจะถูกเรียกใช้เมื่อ การเปลี่ยนแปลงสถานะตัวโหลด ณ จุดนี้ของการโทร หากผู้โทรอยู่ใน สถานะเริ่มต้นและตัวโหลดที่ขอมีอยู่แล้ว และได้สร้าง แล้วระบบจะเรียกใช้ onLoadFinished() ทันที ระหว่างinitLoader() คุณต้องเตรียมตัวให้พร้อมสำหรับกรณีนี้ สำหรับการสนทนาเพิ่มเติมเกี่ยวกับการติดต่อกลับนี้ โปรดดูที่ส่วนเกี่ยวกับ onLoadFinished

เมธอด initLoader() จะแสดงผล Loader ที่สร้างขึ้น แต่คุณไม่จำเป็นต้องบันทึกข้อมูลการอ้างอิง LoaderManager จัดการ อายุการใช้งานของตัวโหลดโดยอัตโนมัติ LoaderManager เริ่มและหยุดการโหลดเมื่อจำเป็น และรักษาสถานะของตัวโหลด และเนื้อหาที่เกี่ยวข้อง

โดยนัยนี้ คุณไม่ค่อยโต้ตอบกับตัวโหลด โดยตรง คุณมักใช้เมธอด LoaderManager.LoaderCallbacks เพื่อแทรกแซงการโหลด ประมวลผลเมื่อมีเหตุการณ์บางอย่างเกิดขึ้น สำหรับการสนทนาในหัวข้อนี้เพิ่มเติม โปรดดูที่ส่วนการใช้ Callback ของ LoaderManager

รีสตาร์ทตัวโหลด

เมื่อคุณใช้ initLoader() เป็น ที่แสดงในส่วนก่อนหน้า จะใช้ตัวโหลดที่มีอยู่ซึ่งมีรหัสที่ระบุ (ถ้ามี) หากไม่มี ให้สร้างบัญชี แต่บางครั้งคุณก็ต้องการละทิ้งข้อมูลเก่า แล้วเริ่มต้นใหม่

หากต้องการทิ้งข้อมูลเก่า ให้ใช้ restartLoader() ตัวอย่างเช่น URL ต่อไปนี้ การรีสตาร์ท 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;
}

ใช้ Callback ของ LoaderManager

LoaderManager.LoaderCallbacks เป็นอินเทอร์เฟซ Callback ที่ช่วยให้ลูกค้าโต้ตอบกับ LoaderManager ได้

เครื่องมือโหลด โดยเฉพาะ CursorLoader ควรจะ เก็บข้อมูลของตนเองไว้หลังจากที่หยุด ซึ่งช่วยให้แอปพลิเคชันต่างๆ สามารถ ข้อมูลในเมธอด onStop() และ onStart() ของกิจกรรมหรือส่วนย่อย เพื่อให้ เมื่อผู้ใช้กลับไปที่แอปพลิเคชัน ก็ไม่ต้องรอให้ข้อมูล โหลดซ้ำ

คุณใช้เมธอด LoaderManager.LoaderCallbacks เพื่อให้ทราบเวลาที่จะสร้างตัวโหลดใหม่และเพื่อบอกแอปพลิเคชันเมื่อโหลดแล้ว ถึงเวลาหยุดใช้ข้อมูลของตัวโหลด

LoaderManager.LoaderCallbacks รวมรายการเหล่านี้ วิธีการ:

  • onCreateLoader(): สร้างอินสแตนซ์และแสดงผล Loader ใหม่สำหรับรหัสที่ระบุ
  • onLoadFinished(): เรียกเมื่อตัวโหลดที่สร้างขึ้นก่อนหน้านี้โหลดเสร็จแล้ว
  • onLoaderReset(): ถูกเรียกเมื่อมีการรีเซ็ตตัวโหลดที่สร้างขึ้นก่อนหน้านี้ ซึ่งทำให้ ไม่มีข้อมูล

วิธีการเหล่านี้จะให้รายละเอียดเพิ่มเติมในส่วนต่อไปนี้

ในCreateLoader

เมื่อคุณพยายามเข้าถึงตัวโหลด เช่น ผ่าน initLoader() โปรแกรมจะตรวจสอบว่า มีตัวโหลดที่ระบุโดยรหัสอยู่แล้ว หากไม่เป็นเช่นนั้น จะเป็นการทริกเกอร์เมธอด LoaderManager.LoaderCallbacks onCreateLoader() ช่วงเวลานี้ คือที่ที่คุณสร้างตัวโหลดใหม่ โดยทั่วไปคือ CursorLoader แต่คุณใช้คลาสย่อย Loader ของตัวเองได้

ในตัวอย่างต่อไปนี้ onCreateLoader() เมธอด Callback จะสร้าง CursorLoader โดยใช้เมธอดตัวสร้าง ซึ่ง ต้องการชุดข้อมูลทั้งหมดที่จำเป็นต่อการค้นหา ContentProvider โดยเฉพาะอย่างยิ่ง จำเป็นต้องมีสิ่งต่อไปนี้

  • uri: URI ของเนื้อหาที่จะดึง
  • การคาดคะเน: รายการคอลัมน์ที่จะแสดงผล ขว้างบอล null จะแสดงผลคอลัมน์ทั้งหมดซึ่งไม่มีประสิทธิภาพ
  • selection: ตัวกรองที่ประกาศแถวที่จะแสดงผล จัดรูปแบบเป็นคำสั่ง WHERE ของ SQL (ยกเว้นตัว 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);
}

รีเซ็ตตัวโหลด

ระบบจะเรียกเมธอดนี้เมื่อมีการรีเซ็ตตัวโหลดที่สร้างขึ้นก่อนหน้านี้ ดังนั้น ทำให้ไม่มีข้อมูล Callback นี้จะช่วยให้คุณทราบว่าข้อมูล กำลังจะเผยแพร่ เพื่อให้คุณสามารถลบการอ้างอิงถึงเนื้อหานั้นได้

การเรียกการติดตั้งใช้งานนี้ 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 เพื่อจัดการการค้นหาจากผู้ให้บริการ

เนื่องจากตัวอย่างนี้มาจากแอปพลิเคชันเพื่อเข้าถึงที่อยู่ติดต่อของผู้ใช้ ไฟล์ Manifest ต้องมีการอนุญาต 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: ตัวอย่างวิธีใช้การควบคุมเพื่อลดจำนวน ของข้อความค้นหาที่ผู้ให้บริการเนื้อหาดำเนินการเมื่อข้อมูลมีการเปลี่ยนแปลง