Trình tải

Kể từ Android 9 (API cấp 28), trình tải sẽ không được dùng nữa. Lựa chọn được đề xuất cho xử lý việc tải dữ liệu trong khi xử lý vòng đời ActivityFragment là sử dụng một 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 nhận biết vòng đời để tải dữ liệu mà bạn có thể sử dụng lại trong nhiều mô hình hiển thị. Bạn cũng có thể kết hợp LiveData bằng cách sử dụng MediatorLiveData. Bất kỳ truy vấn nào có thể quan sát được, chẳng hạn như truy vấn từ Cơ sở dữ liệu Room, có thể dùng để quan sát các thay đổi đối với dữ liệu.

Bạn cũng có thể dùng ViewModelLiveData trong trường hợp bạn không có quyền truy cập vào LoaderManager, chẳng hạn như trong Service. Sử dụng cả hai trong tandem giúp bạn 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ý giao diện người dùng vòng đời. Để tìm hiểu thêm về LiveData, hãy xem Tổng quan về LiveData. Để tìm hiểu thêm về ViewModel, xem thông tin tổng quan về ViewModel.

Loader API cho phép bạn tải dữ liệu từ 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ố vấn đề sau:

  • Nếu bạn tìm nạp dữ liệu trực tiếp trong hoạt động hoặc mảnh, người dùng của bạn thiếu phản hồi do có khả năng hoạt động chậm các truy vấn từ luồng giao diện người dùng.
  • Nếu bạn tìm nạp dữ liệu từ một chuỗi khác, có thể là bằng AsyncTask, thì bạn chịu trách nhiệm quản lý cả chuỗi đó và luồng giao diện người dùng thông qua các sự kiện trong vòng đời mảnh hoặc hoạt động khác nhau, 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à đi kèm những 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 bị 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ấu hình thay đổi để ngăn chặn truy vấn trùng lặp.
  • 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. Ví dụ: CursorLoader tự động đăng ký một ContentObserver để kích hoạt quá trình tải lại khi dữ liệu thay đổi.

Tóm tắt về Loader API

Có thể có nhiều lớp và giao diện cũng sẽ được đưa vào khi sử dụng trình tải trong ứng dụng. Các chỉ số này được tóm tắt trong bảng sau:

Lớp/Giao diện 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 có một LoaderManager có thể quản lý nhiều trình tải.

Để nhận LoaderManager, hãy gọi getSupportLoaderManager() khỏi 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 một trong hai initLoader() hoặc restartLoader() Hệ thống sẽ tự động xác định xem một trình tải có cùng mã nhận dạng số nguyên đã được hay chưa tồn tại và tạo một trình tải mới hoặc sử dụng lại một trình tải hiện có.

LoaderManager.LoaderCallbacks Giao diện này chứa phương thức gọi lại sẽ được gọi khi sự kiện trình tải xảy ra. Giao diện này 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, tạo một đối tượng Loader và trả về đối tượng đó cho hệ thống.
  • onLoadFinished(Loader<D>, D): được gọi khi trình tải đã tải xong dữ liệu. Bạn thường hiển thị dữ liệu cho người dùng trong mã của bạn.
  • onLoaderReset(Loader<D>): được gọi khi một trình tải đã tạo trước đó được đặt lại, khi bạn gọi destroyLoader(int) hoặc khi hoạt động hoặc mảnh bị huỷ bỏ, khiến dữ liệu của mảnh không có sẵn. Trong mã của bạn, 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à đã đă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 mang tính trừu tượng và phân phát làm lớp cơ sở cho tất cả trình tải. Bạn có thể trực tiếp thêm lớp con Loader hoặc sử dụng một trong các tính năng tích hợp sẵn sau đây lớp con để đơn giản hoá việc 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 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 bao gồm những nội dung sau:

Khởi động một 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.

Bạn thường khởi chạy Loader trong phương thức onCreate() của hoạt động hoặc onCreate(). Bạn thực hiện việc này 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 giúp 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 tại cấu trúc (trong ví dụ này là null).
  • Triển khai LoaderManager.LoaderCallbacks, các lệnh gọi LoaderManager để báo cáo sự kiện của trình tải. Trong phần này ví dụ: lớp cục bộ triển khai giao diện LoaderManager.LoaderCallbacks, vì vậy, lớp này truyền một tham chiếu với chính nó, this.

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

  • Nếu đã có trình tải mà mã nhận dạng chỉ định, thì trình tải được tạo gần đây nhất đượ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, initLoader() sẽ kích hoạt LoaderManager.LoaderCallbacks phương thức onCreateLoader(). Đây là nơi bạn triển khai mã để tạo thực thể và trả về một trình tải mới. Để thảo luận thêm, hãy xem phần về onCreateLoader.

Trong cả hai trường hợp, LoaderManager.LoaderCallbacks đã cho triển khai được liên kết với trình tải và được gọi khi các thay đổi về trạng thái của trình tải. Nếu, tại thời điểm của cuộc gọi này, phương thức gọi đang ở trạng thái bắt đầu và trình tải được yêu cầu đã tồn tại và đã 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 về onLoadFinished.

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

Như điều này có nghĩa là bạn hiếm khi tương tác với trình tải trực tiếp. Bạn thường 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 phần Sử dụng lệnh gọi lại LoaderManager.

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

Khi bạn sử dụng initLoader(), như hiển thị trong phần trước, nó sử dụng một trình tải hiện có cùng với mã nhận dạng được chỉ định (nếu có). Nếu chưa có, hệ thống sẽ tạo một tài khoản. 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 sử dụng restartLoader(). Ví dụ: như sau quá trình triển khai SearchView.OnQueryTextListener khởi động lại trình tải khi truy vấn của người dùng thay đổi. Bạn cần khởi động lại trình tải để để có thể sử dụng bộ lọc tìm kiếm đã sửa đổi để tạo một 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ị ngừng. Điều này cho phép các ứng dụng giữ nguyên trên các phương thức onStop()onStart() của hoạt động hoặc mảnh để khi người dùng quay lại một ứng dụng, họ không phải đợi dữ liệu tải lại.

Bạn 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 khi nào đã đến lúc ngừng sử dụng dữ liệu của trình tải.

LoaderManager.LoaderCallbacks bao gồm những điều sau phương thức:

  • onLoadFinished(): được gọi khi một trình tải đã 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 đó được đặt lại, khiến cho không có sẵn dữ liệu.

Các phương pháp này được mô tả chi tiết hơn trong các phần sau.

onCreateLoader

Khi bạn cố gắng truy cập vào một trình tải, chẳng hạn như thông qua initLoader(), trình tải này sẽ kiểm tra xem liệu trình tải được chỉ định theo mã nhận dạng đã tồn tại. Nếu không, lệnh này sẽ kích hoạt phương thức LoaderManager.LoaderCallbacks onCreateLoader(). Chiến dịch này là nơi bạn tạo một trình tải mới. Thường thì đâ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, onCreateLoader() phương thức gọi lại này 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 thông tin hoàn chỉnh cần thiết để thực hiện truy vấn đối với ContentProvider. Cụ thể, ứng dụng này cần:

  • uri: URI cho nội dung cần truy xuất.
  • projection: danh sách các cột sẽ trả về. Cầu thủ chuyền bóng null trả về tất cả các cột và điều này không hiệu quả.
  • selection: bộ lọc khai báo hàng nào cần trả về, có định dạng là mệnh đề SQL WHERE (không bao gồm chính WHERE). Cầu thủ chuyền bóng null trả về tất cả các hàng cho URI đã cho.
  • selectionArgs: nếu bạn đưa dấu ?s vào lựa chọn, chúng được thay thế bằng các giá trị lấy từ selectionArgs theo thứ tự xuất hiện trong 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 SQL Mệnh đề ORDER BY (không bao gồm chính ORDER BY). Chuyền thành công 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");
}

onLoadĐã hoàn tất

Phương thức này được gọi khi một trình tải đã tạo trước đó hoàn tất lượt 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 gần đây nhất được cung cấp cho trình tải này. Tại thời điểm này, hãy xoá tất cả việc sử dụng dữ liệu cũ vì dữ liệu này sắp được phát hành. Nhưng không phát hành dữ liệu chính bạn—trình tải sở hữu và xử lý việc đó.

Trình tải sẽ huỷ bỏ dữ liệu sau khi biết rằng ứng dụng không còn tồn tại đang sử dụng nó. Ví dụ: nếu dữ liệu là con trỏ từ CursorLoader, đừng tự gọi close(). Nếu con trỏ đang được đặt trong CursorAdapter, hãy sử dụng phương thức swapCursor() để Cursor cũ chưa đóng, như 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);
}

onLoaderReset

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

Phương thức triển khai này gọi swapCursor() có 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 các kết quả của một truy vấn với trình cung cấp nội dung danh bạ. Lớp này sử dụng CursorLoader để quản lý truy vấn trên trình cung cấp.

Do ví dụ này là từ một ứng dụng để truy cập vào danh bạ của người dùng, tệp kê khai 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:

  • LoaderCursor: 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 sử dụng CursorLoader để truy xuất dữ liệu của nhà cung cấp danh bạ.
  • LoaderThrottle: ví dụ về cách sử dụng tính năng điều tiết để giảm số lượng của các truy vấn mà 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.