Life Before Loaders (part 1)
This post gives a brief introduction to Loaders and the LoaderManager. The first section describes how data was loaded prior to the release of Android 3.0, pointing out out some of the flaws of the pre-Honeycomb APIs. The second section defines the purpose of each class and summarizes their powerful ability in asynchronously loading data.
What Will I Learn?
public void startManagingCursor(Cursor)
public Cursor managedQuery(Uri, String, String, String, String)
Requirements
Android Studio 2.3 and above
Basic Knowledge of native Android development using Java
Difficulty
Intermediate
Tutorial Contents
Before Android 3.0, many Android applications lacked in responsiveness. UI interactions glitched, transitions between activities lagged, and ANR (Application Not Responding) dialogs rendered apps totally useless. This lack of responsiveness stemmed mostly from the fact that developers were performing queries on the UI thread—a very poor choice for lengthy operations like loading data.
While the documentation has always stressed the importance of instant feedback, the pre-Honeycomb APIs simply did not encourage this behavior. Before Loaders, cursors were primarily managed and queried for with two (now deprecated) Activity methods:
public void startManagingCursor(Cursor)
Tells the activity to take care of managing the cursor’s lifecycle based on the activity’s lifecycle. The cursor will automatically be deactivated (deactivate()) when the activity is stopped, and will automatically be closed (close()) when the activity is destroyed. When the activity is stopped and then later restarted, the Cursor is re-queried (requery()) for the most up-to-date data.
public Cursor managedQuery(Uri, String, String, String, String)
A wrapper around the ContentResolver’s query() method. In addition to performing the query, it begins management of the cursor (that is, startManagingCursor(cursor) is called before it is returned).
While convenient, these methods were deeply flawed in that they performed queries on the UI thread. What’s more, the “managed cursors” did not retain their data across Activity configuration changes. The need to requery() the cursor’s data in these situations was unnecessary, inefficient, and made orientation changes clunky and sluggish as a result.
The Problem with “Managed Cursors”
Let’s illustrate the problem with “managed cursors” through a simple code sample. Given below is a ListActivity that loads data using the pre-Honeycomb APIs. The activity makes a query to the ContentProvider and begins management of the returned cursor. The results are then bound to a SimpleCursorAdapter, and are displayed on the screen in a ListView. The code has been condensed for simplicity.
public class SampleListActivity extends ListActivity {
private static final String[] PROJECTION = new String[] {"_id", "text_column"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Performs a "managed query" to the ContentProvider. The Activity
// will handle closing and requerying the cursor.
//
// WARNING!! This query (and any subsequent re-queries) will be
// performed on the UI Thread!!
Cursor cursor = managedQuery(
CONTENT_URI, // The Uri constant in your ContentProvider class
PROJECTION, // The columns to return for each data row
null, // No where clause
null, // No where clause
null); // No sort order
String[] dataColumns = { "text_column" };
int[] viewIDs = { R.id.text_view };
// Create the backing adapter for the ListView.
//
// WARNING!! While not readily obvious, using this constructor will
// tell the CursorAdapter to register a ContentObserver that will
// monitor the underlying data source. As part of the monitoring
// process, the ContentObserver will call requery() on the cursor
// each time the data is updated. Since Cursor#requery() is performed
// on the UI thread, this constructor should be avoided at all costs!
SimpleCursorAdapter adapter = new SimpleCursorAdapter(
this, // The Activity context
R.layout.list_item, // Points to the XML for a list item
cursor, // Cursor that contains the data to display
dataColumns, // Bind the data in column "text_column"...
viewIDs); // ...to the TextView with id "R.id.text_view"
// Sets the ListView's adapter to be the cursor adapter that was
// just created.
setListAdapter(adapter);
}
}
There are three problems with the code above. If you have understood this post so far, the first two shouldn’t be difficult to spot:
managedQuery performs a query on the main UI thread. This leads to unresponsive apps and should no longer be used.
As seen in the Activity.java source code, the call to managedQuery begins management of the returned cursor with a call to startManagingCursor(cursor). Having the activity manage the cursor seems convenient at first, as we no longer need to worry about deactivating/closing the cursor ourselves. However, this signals the activity to call requery() on the cursor each time the activity returns from a stopped state, and therefore puts the UI thread at risk. This cost significantly outweighs the convenience of having the activity deactivate/close the cursor for us.
The SimpleCursorAdapter constructor is deprecated and should not be used. The problem with this constructor is that it will have the SimpleCursorAdapter auto-requery its data when changes are made. More specifically, the CursorAdapter will register a ContentObserver that monitors the underlying data source for changes, calling requery() on its bound cursor each time the data is modified. The standard constructor should be used instead (if you intend on loading the adapter’s data with a CursorLoader, make sure you pass 0 as the last argument). Don’t worry if you couldn’t spot this one… it’s a very subtle bug.
With the first Android tablet about to be released, something had to be done to encourage UI-friendly development. The larger, 7-10” Honeycomb tablets called for more complicated, interactive, multi-paned layouts. Further, the introduction of the Fragment meant that applications were about to become more dynamic and event-driven. A simple, single-threaded approach to loading data could no longer be encouraged. Thus, the Loader and the LoaderManager were born.
Conclusion
Since the introduction of Loaders in Honeycomb and Compatibility Library, Android applications have changed for the better. Making use of the now deprecated startManagingCursor and managedQuery methods are extremely discouraged; not only do they slow down your app, but they can potentially bring it to a screeching halt. Loaders, on the other hand, significantly speed up the user experience by offloading the work to a separate background thread.
In the next post (titled Understanding the LoaderManager), we will go more in-depth on how to fix these problems by completing the transition from “managed cursors” to making use of Loaders and the LoaderManager.
Don’t forget to upvote this blog in the top right corner if you found this helpful!
Posted on Utopian.io - Rewarding Open Source Contributors
Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
https://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html
The contribution has been rejected as it is plagiarized from : https://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html
[utopian moderator]