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 Activity
và Fragment
là sử dụng tổ hợp các đối tượng ViewModel
và LiveData
.
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.
ViewModel
và LiveData
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 Để bắt đầu tải dữ liệu từ một trình tải, hãy gọi |
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:
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:
FragmentActivity
hoặcFragment
.- Một thực thể của
LoaderManager
. CursorLoader
để tải dữ liệu đượcContentProvider
hỗ trợ. Ngoài ra, bạn có thể triển khai lớp conLoader
hoặcAsyncTaskLoader
của riêng mình để tải dữ liệu từ một số nguồn khác.- Triển khai cho
LoaderManager.LoaderCallbacks
. Đây là nơi bạn tạo các trình tải mới và quản lý các tệp tham chiếu đến các trình tải hiện có. - Một cách hiển thị dữ liệu của trình tải, chẳng hạn như
SimpleCursorAdapter
. - Một nguồn dữ liệu, chẳng hạn như
ContentProvider
, khi sử dụngCursorLoader
.
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.LoaderCallbacks
màLoaderManager
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ệnLoaderManager.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ứcLoaderManager.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ầnonCreateLoader
.
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()
và 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:
onCreateLoader()
: tạo thực thể và trả về mộtLoader
mới cho mã nhận dạng đã cho.
-
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.