How to Use Loaders in Android
With the introduction of Honeycomb Loaders became the preferred way to access data of databases or content providers. They load data asynchronously and notify listeners when the results are ready.
Google did not only introduce Loaders but also deprecated the previous way to handle a Cursor within your activities. You shouldn't use startManagingCursor()
managedQuery()
in your projects anymore.
With managed cursors queries and requeries are executed on the UI thread. This could cause the app to feel unresponsive or to even display an ANR error message. With Loaders your queries will no longer run on the UI thread and your app remains responsive.
In this post I introduce the classes that form the Loader API and show you how to use them.
Class | Usage |
---|---|
LoaderManager | Manages your Loaders for you. Responsible for dealing with the Activity or Fragment lifecycle |
LoaderManager.LoaderCallbacks | A callback interface you must implement |
Loader | The base class for all Loaders |
AsyncTaskLoader | An implementation that uses an AsyncTask to do its work |
CursorLoader | A subclass of AsyncTaskLoader for accessing ContentProvider data |
In the following sections I describe most of these classes and what you need to know about them - starting with the LoaderManager.
LoaderManager
This class keeps your Loaders in line with the lifecycle of your activities or fragments. If Android destroys your fragments or activities, the LoaderManager
notifies the managed loaders to free up their resources. The LoaderManager
is also responsible for retaining your data on configuration changes like a change of orientation and it calls the relevant callback methods when the data changes. In short: The LoaderManager
is way more powerful than the old startManagingCursor()
or managedQuery()
methods.
You do not instantiate the LoaderManager
yourself. Instead you simply call getLoaderManager()
from within your activity or your fragment to get hold of it.
Most often you are only interested in two methods of the manager:
initLoader()
andrestartLoader()
initLoader()
The initLoader()
method adds a Loader
to the LoaderManager
:
getLoaderManager().initLoader(LIST_ID, null, this);
It takes three arguments:
- a unique ID for this loader,
- an optional
Bundle
with arguments for your Loader and
You might need the ID for further method calls. So using a final static field for the ID makes your code more readable. The Bundle can be used to pass additional arguments to your Loader, but isn't used by the CursorLoader
. The third argument, the callback interface, will be covered in detail later on.
The initLoader()
method creates a new Loader only if for this ID none has been created previously. Keep in mind that Android deals with configuration changes for you, thus a simple change in orientation is enough to trigger a new call to initLoader()
. In this case the method returns the existing instance and your query is not executed again.
restartLoader()
Because Android doesn't execute the query again, you need a way to re-initialize the Loader when data, that is used to build the query, changes. Typical examples are search queries.
You reset your Loader by using the restartLoader()
method. It takes the same parameters as initLoader()
. Of course you have to use the same ID you used for initializing.
getLoaderManager().restartLoader(LIST_ID, null, this);
LoaderManager.LoaderCallbacks
The interface LoaderCallbacks
defines methods you must implement to create your Loader, to deal with the results and to clean up resources.
Since the interface is parameterized you must specify the type of data your Loader holds. Most often the type will be Cursor
:
public class YourFragment extends Fragment
implements LoaderCallbacks<Cursor> {
//...
}
The methods you have to implement are:
- onCreateLoader(),
- onLoadFinished() and
- onLoadReset()
In the next sections I show what to do in each of these callback methods.
onCreateLoader()
The LoaderManager
calls this method when you call initLoader()
for the first time. As mentioned, the manager only calls this method if no loader for the given ID exists.
The method gets an int value and a Bundle passed in. These are the same values you used for your initLoader() call.
A typical example creating a CursorLoader
looks like this:
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader loader = new CursorLoader(
this.getActivity(),
SOME_CONTENT_URI,
projection,
selection,
selectionArgs,
sortOrder);
return loader;
}
As you can see the parameters are a Context
object plus those of the ContentResolver's query()
method. If you're not familiar with these arguments, I recommend you read my post about accessing content providers.
If you need to track multiple queries and thus use different IDs for your Loaders, all you need to add is a simple case- or if-else-branch.
onLoadFinished()
This method is the most interesting one. Here you update the UI based on the results of your query.
For ListAdapters
you simply swap the cursor of the adapter as described in the section "Changes needed for CursorAdapters".
For all other cases you have to get references to the view elements and set their value according to the result.
This is how it looks in the sample project:
public void onLoadFinished(
Loader<Cursor> loader,
Cursor cursor) {
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
int idIndex =
cursor.getColumnIndex(LentItems._ID);
int nameIndex =
cursor.getColumnIndex(LentItems.NAME);
int borrowerIndex =
cursor.getColumnIndex(LentItems.BORROWER);
this.itemId = cursor.getLong(idIndex);
String name = cursor.getString(nameIndex);
String borrower = cursor.getString(borrowerIndex);
((EditText)findViewById(R.id.name)).
setText(name);
((EditText)findViewById(R.id.person)).
setText(borrower);
}
}
onLoadReset()
This method allows you to release any resources you hold, so that the Loader can free them. You can set any references to the cursor object you hold to null.
But do not close the cursor - the Loader does this for you.
Loader, AsyncTaskLoader and CursorLoader
The Loader interface and its implementations are not very interesting - unless you write your own custom Loaders. You have to create a Loader
of course. But other than using the constructor of CursorLoader
, you normally do not interact with these objects yourself.
If you use multiple Loaders you need to access the ID in the callback methods. You can do so by calling getId()
on the loader passed in to the callbacks.
If you want to write a custom Loader yourself, please have a look at Alex Lockwood's tutorial on implementing loaders.
Changes needed for CursorAdapters
An important use of cursors in Android is to use a CursorAdapter
as data source for ListViews
, AutoCompleteTextViews
and so on. When working with Loaders you have to adapt the old way slightly.
First of all: You do not have a Cursor object before the onLoadFinished()
method of your callback has been called. In other words: The cursor is not ready when you create the adapter. Thus you create the adapter using null
for the cursor argument:
SimpleCursorAdapter adapter =
new SimpleCursorAdapter(
getApplicationContext(),
android.R.layout.simple_list_item_1,
null,
columns,
layoutIds,
0);
When the cursor is finally available you have to add it. You do this by calling swapCursor()
on your adapter and passing in the cursor object of your callback method:
public void onLoadFinished(
Loader<Cursor> loader,
Cursor cursor) {
((SimpleCursorAdapter)this.getListAdapter()).
swapCursor(cursor);
}
And to clean up resources in the onLoadReset()
method you also use swapCursor()
, this time passing in a null
value:
public void onLoaderReset(Loader<Cursor> loader) {
((SimpleCursorAdapter)this.getListAdapter()).
swapCursor(null);
}
Use the Support Library for older Android versions
To support newer features on older Android versions, Google provides the Support Library. The two main components this library contains are Fragments and Loaders.
You can and should use this library for projects that must support older versions. All reasons for using the Loader-API are valid for pre-Honeycomb devices as well.
But you have to keep an eye on the import statements. All import statements for classes of the Support Library begin with android.support.v4
:
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
Don't mix classes of the support library with normal classes. The compiler will spot most problems for you and then there is also Lint to help you. But if you ever wonder why some code is marked as invalid, this most likely is the reason.
To use Loaders for older Android versions, you need to use activities or fragments of the Support Library. The normal ones do not have the getLoaderManager()
method before Honeycomb. You project would still compile (if your build target is at least SDK 11) but at runtime you would get a NoSuchMethodError
on older devices.
Using Loaders to access your SQLiteDatabase
Since Android's CursorLoader
is only for Cursors returned by content providers we need another Loader implementation if we want to use SQLite directly.
Thankfully Mark Murphy has written a library that offers enhancements to the Loader framework. This library also contains a SQLiteCursorLoader
.
To use Mark Murphy's SQLiteCursorLoader
you have to create an SQL select
statement and pass it to the constructor.The constructor also expects an SQLiteOpenHelper
object. An example of the onCreateLoader() method could look like this:
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String rawQuery = "SELECT ...";
String[] queryParams = // to substitute placeholders
SQLiteCursorLoader loader =
new SQLiteCursorLoader(
getActivity().getApplicationContext(),
yourSqliteOpenHelper,
rawQuery,
queryParams);
return loader;
}
But there is more to it. You have to use this loader also for deleting, updating or inserting rows as well. Only by doing so, the loader knows about a data change and can call the correct callback methods.
I won't go into these details here. See the project's github page or have a look at Mark Murphy's sample project.
If you intend to use this library, don't use the jar-file offered on the project page. It is outdated. Instead clone the git repository and add the project to your IDE directly.
When not to use Loaders
On reddit, cokacokacoh noted that my post missed a section on when not to use Loaders. He was right of course. So I added the following paragraph to this post.
As cokacokacoh points out, you shouldn't use Loaders if you need the background tasks to complete. Android destroys Loaders together with the Activities/Fragments they belong to. If you want to do some tasks, that have to run until completion, do not use Loaders. You should use services for this kind of stuff instead.
Keep in mind that Loaders are special components to help you create responsive UIs and to asynchronously load data that this UI component needs. That's the reason why Loaders are tied to the lifecycle of their creating components. Do not try to abuse them for anything else!
Lessons learned
In this blog post you have seen how to use the Loader framework. And since this framework is available for older Android versions using the Support Library there is no reason not to use it.
The CursorLoader
provided by Android is useful for content providers. But not all projects use these. If your project uses SQLite directly you can use Mark Murphys SQLiteCursorLoader
as shown above.
I didn't cover how to write Loaders on your own. If you need to do so, +Alex Lockwood has a nice tutorial on implementing loaders.
Please let me know in the comments if this post was helpful or if you have any questions left. And don't forget to plus one or tweet this post, if you liked it 🙂
相關推薦
How to Use Loaders in Android
With the introduction of Honeycomb Loaders became the preferred way to access data of databases or content providers.
Why (and how) to use eslint in your project
Why (and how) to use eslint in your projectThis story was written by Sam Roberts, a Senior Software Engineer at IBM Canada. It was first published in IBM d
How to use AI in the insurance value chain: customer service and policy administration
How do you approach customer service and policy administration within your organization? In this blog post, I'll demonstrate how artificial intelligence (A
How to use AI in a small business
If you are a small business owner, you may wonder if it is possible to implement AI in your business; you may even feel very challenged by the idea. There
How to use Git in a secure way
How to use Git in a secure wayWe live in a world where it is hard not to know Git, the most popular Distributed Version Control System (DVCS). Free and ope
How to use DeepLab in TensorFlow for object segmentation using Deep Learning
How to use DeepLab in TensorFlow for object segmentation using Deep LearningModifying the DeepLab code to train on your own dataset for object segmentation
How to use Retrofit on android with Kotlin (KAD 21)
This is just one more example about how in Kotlin we can continue to use the same libraries we’ve always used in Java for Android. Retrofit is a libr
how to use composer in fiddler
aix cti request com connect req cnblogs 表示 技術分享 https://www.cnblogs.com/youxin/p/3570310.html http://docs.telerik.com/fiddler/generate-t
how to use Inspector in fiddler
date perm alt com ons blog ima ddl content 打開fiddler之後,會自動捕獲本機的http請求,以列表的形式顯示在左側 雙擊左側列表中的某一個request,右側會自動切換到Inspectors窗口。 右側上半部分是reque
How to Use Instruments in Xcode-(轉載)
This is a blog post by iOS Tutorial Team member Matt Galloway, founder of SwipeStack, a mobile development team based in London, UK. Y
How to use VirtualBox in Terminal / Command line
How to manage VirtualBox in command line or terminal. VBoxManage is the command which is used to manage VirtualBox in commandline.
How To Use Retrofit Library In Your Android App
Retrofit library is a Type-safe REST client for android and Java, courtesy of Square Inc. Most modern android apps make HTTP requests to some remote s
Android: How to Use Two Data Sources in ListViews?
Sometimes in our apps we have the need to link to data of other sources. You have a database of your own for your app
How to use *args and **kwargs in Python
這篇文章寫的滿好的耶,結論: 1星= array, 2星=dictionary. 1星範例: def test_var_args(farg, *args): print "formal arg:", farg for arg in args: print "an
How To Use Simple Factory Design Pattern In Java
Simple Factory Design Pattern is one of the many design patterns – best practices on how to solve common problems. Design Patterns were made popular by
how to use “request” object within a function in jsp
request is accessible inside the scriptlet expressions, because it’s an argument of the method in which these expressions are evaluated (_jspService). But
How to use APIs with Pandas and store the results in Redshift
How to use APIs with Pandas and store the results in RedshiftHere is an easy tutorial to help understand how you can use Pandas to get data from a RESTFUL
How to Use IoT Datasets in #AI Applications
Recently, google launched a Dataset search – which is a great resource to find Datasets. In this post, I list some IoT datasets which can be used for Machi
How to use Android Studio's SVG-to-VectorDrawable converter from the command line
Since the very beginning, one of the most annoying aspects of using VectorDrawables on Android has been the lack of a reliable SVG converter. Goog