Trình tải

Kể từ Android 9 (API cấp 28), trình tải không còn được dùng nữa. Tuỳ chọn đề xuất để xử lý việc tải dữ liệu trong khi xử lý vòng đời ActivityFragment là sử dụng tổ hợp các đối tượng ViewModelLiveData. Các mô hình khung hiển thị vẫn tồn tại sau khi thay đổi cấu hình, chẳng hạn như trình tải, nhưng với ít mã nguyên mẫu hơn. LiveData cung cấp cách tải dữ liệu nhận biết được vòng đời mà bạn có thể sử dụng lại trong nhiều mô hình khung hiển thị. Bạn cũng có thể kết hợp LiveData bằng MediatorLiveData. Bạn có thể dùng mọi truy vấn có thể quan sát, chẳng hạn như các truy vấn từ cơ sở dữ liệu Room, để quan sát các thay đổi đối với dữ liệu.

ViewModelLiveData cũng có sẵn trong những trường hợp mà bạn không có quyền truy cập vào LoaderManager, chẳng hạn như trong Service. Việc sử dụng hai chức năng này song song mang lại một cách dễ dàng để truy cập vào dữ liệu mà ứng dụng của bạn cần mà không phải xử lý vòng đời của giao diện người dùng. Để tìm hiểu thêm về LiveData, hãy xem tổng quan về LiveData. Để tìm hiểu thêm về ViewModel, hãy xem tổng quan về ViewModel.

Loader API (API Trình tải) cho phép bạn tải dữ liệu từ một trình cung cấp nội dung hoặc nguồn dữ liệu khác để hiển thị trong FragmentActivity hoặc Fragment.

Nếu không có trình tải, bạn có thể gặp phải một số sự cố sau:

  • Nếu bạn tìm nạp dữ liệu ngay trong hoạt động hoặc mảnh, thì người dùng sẽ không phản hồi nhanh được do thực hiện các truy vấn có thể bị chậm từ luồng giao diện người dùng.
  • Nếu tìm nạp dữ liệu từ một luồng khác, có thể là bằng AsyncTask, thì bạn sẽ chịu trách nhiệm quản lý cả luồng đó và luồng giao diện người dùng thông qua nhiều sự kiện trong vòng đời của mảnh hoặc hoạt động, chẳng hạn như onDestroy() và các thay đổi về cấu hình.

Trình tải giải quyết những vấn đề này và mang lại nhiều lợi ích khác:

  • Trình tải chạy trên các luồng riêng biệt để ngăn giao diện người dùng (UI) chậm hoặc không phản hồi.
  • Trình tải đơn giản hoá việc quản lý luồng bằng cách cung cấp các phương thức gọi lại khi sự kiện xảy ra.
  • Trình tải vẫn duy trì và lưu kết quả vào bộ nhớ đệm khi có thay đổi về cấu hình để ngăn truy vấn trùng lặp.
  • Các trình tải có thể triển khai một trình quan sát để theo dõi những thay đổi trong nguồn dữ liệu cơ bản. Ví dụ: CursorLoader tự động đăng ký ContentObserver để kích hoạt quá trình tải lại khi dữ liệu thay đổi.

Tóm tắt về API trình tải

Có nhiều lớp và giao diện có thể liên quan khi sử dụng trình tải trong một ứng dụng. Bảng tóm tắt về các lớp và giao diện này được tóm tắt trong bảng sau:

Lớp/Giao diện Nội dung mô tả
LoaderManager Một lớp trừu tượng liên kết với FragmentActivity hoặc Fragment để quản lý một hoặc nhiều thực thể Loader. Chỉ có một LoaderManager cho mỗi hoạt động hoặc mảnh, nhưng một LoaderManager có thể quản lý nhiều trình tải.

Để nhận LoaderManager, hãy gọi getSupportLoaderManager() từ hoạt động hoặc mảnh.

Để bắt đầu tải dữ liệu từ một trình tải, hãy gọi initLoader() hoặc restartLoader(). Hệ thống tự động xác định xem một trình tải có cùng mã số nguyên đã tồn tại hay chưa, rồi tạo một trình tải mới hoặc sử dụng lại trình tải hiện có.

LoaderManager.LoaderCallbacks Giao diện này chứa các phương thức gọi lại được gọi khi xảy ra các sự kiện của trình tải. Giao diện xác định 3 phương thức gọi lại:
  • onCreateLoader(int, Bundle): được gọi khi hệ thống cần tạo một trình tải mới. Trong mã của bạn, hãy tạo một đối tượng Loader và trả về đối tượng đó về hệ thống.
  • onLoadFinished(Loader<D>, D): được gọi khi trình tải đã tải xong dữ liệu. Thường thì bạn cho người dùng thấy dữ liệu trong mã của mình.
  • onLoaderReset(Loader<D>): được gọi khi một trình tải đã tạo trước đó đang được đặt lại, khi bạn gọi destroyLoader(int) hoặc khi hoạt động hay mảnh bị huỷ khiến cho dữ liệu tương ứng không có sẵn. Trong mã của bạn, hãy xoá mọi tham chiếu đến dữ liệu của trình tải.
Hoạt động hoặc mảnh của bạn thường triển khai giao diện này và giao diện được đăng ký khi bạn gọi initLoader() hoặc restartLoader().
Loader Trình tải thực hiện việc tải dữ liệu. Lớp này là lớp trừu tượng và đóng vai trò là lớp cơ sở cho tất cả các trình tải. Bạn có thể trực tiếp phân lớp con Loader hoặc sử dụng một trong các lớp con tích hợp sẵn sau đây để đơn giản hoá quá trình triển khai:

Các phần sau đây cho bạn biết cách sử dụng các lớp và giao diện này trong một ứng dụng.

Sử dụng trình tải trong một ứng dụng

Phần này mô tả cách sử dụng trình tải trong ứng dụng Android. Một ứng dụng sử dụng trình tải thường có những thành phần sau:

Khởi động trình tải

LoaderManager quản lý một hoặc nhiều thực thể Loader trong một FragmentActivity hoặc Fragment. Chỉ có một LoaderManager cho mỗi hoạt động hoặc mảnh.

Thông thường, bạn sẽ khởi động một Loader trong phương thức onCreate() của hoạt động hoặc phương thức onCreate() của mảnh. Bạn thực hiện như sau:

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);

Phương thức initLoader() nhận các thông số sau:

  • Mã nhận dạng duy nhất xác định trình tải. Trong ví dụ này, mã nhận dạng là 0.
  • Các đối số không bắt buộc để cung cấp cho trình tải khi xây dựng (trong ví dụ này là null).
  • Phương thức triển khai LoaderManager.LoaderCallbacksLoaderManager gọi để báo cáo các sự kiện của trình tải. Trong ví dụ này, lớp cục bộ triển khai giao diện LoaderManager.LoaderCallbacks, vì vậy, lớp này sẽ truyền tham chiếu đến chính nó, this.

Lệnh gọi initLoader() đảm bảo rằng một trình tải được khởi chạy và hoạt động. Có hai kết quả có thể xảy ra:

  • Nếu trình tải do mã nhận dạng chỉ định đã tồn tại, thì trình tải được tạo gần đây nhất sẽ được sử dụng lại.
  • Nếu trình tải do mã nhận dạng chỉ định không tồn tại, thì initLoader() sẽ kích hoạt phương thức LoaderManager.LoaderCallbacks onCreateLoader(). Đây là nơi bạn triển khai mã để tạo thực thể và trả về trình tải mới. Để thảo luận thêm, hãy xem phần onCreateLoader.

Trong cả hai trường hợp, quy trình triển khai LoaderManager.LoaderCallbacks đã cho được liên kết với trình tải và được gọi khi trạng thái trình tải thay đổi. Nếu tại thời điểm thực hiện lệnh gọi này, phương thức gọi ở trạng thái bắt đầu và trình tải được yêu cầu đã tồn tại đồng thời đã tạo dữ liệu, thì hệ thống sẽ gọi onLoadFinished() ngay lập tức trong initLoader(). Bạn phải chuẩn bị cho điều này xảy ra. Để thảo luận thêm về lệnh gọi lại này, hãy xem phần onLoadFinished.

Phương thức initLoader() trả về Loader đã được tạo, nhưng bạn không cần ghi lại thông tin tham chiếu đến đối tượng đó. LoaderManager tự động quản lý tuổi thọ của trình tải. LoaderManager bắt đầu và ngừng tải khi cần thiết, đồng thời duy trì trạng thái của trình tải và nội dung liên quan.

Như vậy, bạn hiếm khi tương tác trực tiếp với trình tải. Thường thì bạn sử dụng các phương thức LoaderManager.LoaderCallbacks để can thiệp vào quá trình tải khi các sự kiện cụ thể xảy ra. Để thảo luận thêm về chủ đề này, hãy xem mục Sử dụng lệnh gọi lại LoaderManager.

Khởi động lại trình tải

Khi sử dụng initLoader(), như trình bày ở phần trước, bạn sẽ dùng một trình tải hiện có kèm theo mã nhận dạng được chỉ định (nếu có). Nếu không có, Analytics sẽ tạo một URL. Nhưng đôi khi bạn muốn loại bỏ dữ liệu cũ và bắt đầu lại.

Để loại bỏ dữ liệu cũ, hãy dùng restartLoader(). Ví dụ: quy trình triển khai SearchView.OnQueryTextListener sau đây sẽ khởi động lại trình tải khi truy vấn của người dùng thay đổi. Trình tải cần được khởi động lại để có thể sử dụng bộ lọc tìm kiếm đã sửa đổi nhằm thực hiện truy vấn mới.

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;
}

Sử dụng các lệnh gọi lại LoaderManager

LoaderManager.LoaderCallbacks là giao diện gọi lại cho phép ứng dụng tương tác với LoaderManager.

Các trình tải, cụ thể là CursorLoader, dự kiến sẽ giữ lại dữ liệu sau khi bị dừng. Điều này cho phép ứng dụng lưu giữ dữ liệu trên các phương thức onStop()onStart() của hoạt động hoặc mảnh. Nhờ đó, khi người dùng quay lại ứng dụng, họ không phải đợi dữ liệu tải lại.

Bạn sử dụng các phương thức LoaderManager.LoaderCallbacks để biết thời điểm tạo trình tải mới và cho ứng dụng biết thời điểm cần ngừng sử dụng dữ liệu của trình tải.

LoaderManager.LoaderCallbacks bao gồm các phương thức sau:

  • onLoadFinished(): được gọi khi một trình tải được tạo trước đó hoàn tất quá trình tải.
  • onLoaderReset(): được gọi khi một trình tải đã tạo trước đó đang được đặt lại, khiến dữ liệu của trình tải đó không hoạt động.

Những phương thức này được mô tả chi tiết hơn trong các phần sau.

onCreateLoader

Khi bạn cố truy cập vào một trình tải (chẳng hạn như thông qua initLoader()), hệ thống sẽ kiểm tra xem trình tải mà mã nhận dạng chỉ định có tồn tại hay không. Nếu không, thao tác này sẽ kích hoạt phương thức LoaderManager.LoaderCallbacks onCreateLoader(). Đây là nơi bạn tạo một trình tải mới. Thông thường, đây là CursorLoader, nhưng bạn có thể triển khai lớp con Loader của riêng mình.

Trong ví dụ sau, phương thức gọi lại onCreateLoader() sẽ tạo một CursorLoader bằng phương thức hàm khởi tạo. Phương thức này yêu cầu tập hợp đầy đủ thông tin cần thiết để thực hiện truy vấn đến ContentProvider. Cụ thể, mô-đun này cần những yếu tố sau:

  • uri: URI cho nội dung cần truy xuất.
  • projection: danh sách các cột cần trả về. Thao tác truyền null sẽ trả về tất cả các cột. Điều này không hiệu quả.
  • selection (lựa chọn): bộ lọc khai báo hàng nào cần trả về, được định dạng thành mệnh đề SQL WHERE (ngoại trừ chính WHERE). Thao tác truyền null sẽ trả về tất cả các hàng cho URI nhất định.
  • selectionArgs: nếu bạn đưa ?s vào vùng lựa chọn, thì các ?s sẽ được thay thế bằng các giá trị từ selectionArgs theo thứ tự xuất hiện trong vùng lựa chọn. Các giá trị được liên kết dưới dạng chuỗi.
  • sortOrder: cách sắp xếp các hàng, được định dạng dưới dạng mệnh đề ORDER BY (SẮP XẾP THEO) của SQL (không bao gồm chính ORDER BY) Việc truyền null sử dụng thứ tự sắp xếp mặc định (có thể không theo thứ tự).

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");
}

Đã hoàn tất onLoad

Phương thức này được gọi khi một trình tải đã tạo trước đó hoàn tất quá trình tải. Phương thức này đảm bảo sẽ được gọi trước khi phát hành dữ liệu cuối cùng được cung cấp cho trình tải này. Tại thời điểm này, hãy xoá mọi hoạt động sử dụng dữ liệu cũ vì dữ liệu đó sẽ được phát hành. Tuy nhiên, đừng tự huỷ bỏ dữ liệu – trình tải sở hữu và xử lý dữ liệu đó.

Trình tải sẽ huỷ bỏ dữ liệu khi biết rằng ứng dụng không còn dùng nữa. Ví dụ: nếu dữ liệu là con trỏ từ CursorLoader, đừng tự gọi close() trên dữ liệu đó. Nếu con trỏ đang được đặt vào CursorAdapter, hãy sử dụng phương thức swapCursor() để Cursor cũ không bị đóng, như minh hoạ trong ví dụ sau:

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);
}

onResetLoader

Phương thức này được gọi khi một trình tải đã tạo trước đó đang được đặt lại, do đó khiến dữ liệu của phương thức không có sẵn. Lệnh gọi lại này cho bạn biết thời điểm dữ liệu sắp được phát hành để có thể xoá thông tin tham chiếu đến dữ liệu đó.

Cách triển khai này gọi swapCursor() với giá trị 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);
}

Ví dụ

Ví dụ: dưới đây là cách triển khai đầy đủ của Fragment hiển thị ListView chứa kết quả của truy vấn đối với trình cung cấp nội dung danh bạ. Phương thức này sử dụng CursorLoader để quản lý truy vấn trên trình cung cấp.

Vì ví dụ này là của một ứng dụng truy cập vào danh bạ của người dùng, nên tệp kê khai của ứng dụng đó phải bao gồm quyền 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);
    }
}

Ví dụ khác

Các ví dụ sau đây minh hoạ cách sử dụng trình tải:

  • Trình tải con trỏ: một phiên bản hoàn chỉnh của đoạn mã trước đó.
  • Truy xuất danh sách người liên hệ: hướng dẫn từng bước sử dụng CursorLoader để truy xuất dữ liệu từ trình cung cấp danh bạ.
  • LoaderThrottle: một ví dụ về cách sử dụng chế độ điều tiết để giảm số lượng truy vấn mà một trình cung cấp nội dung thực hiện khi dữ liệu của trình cung cấp đó thay đổi.