CursorLoader usage without ContentProvider
I wrote a simple CursorLoader that does not need a content provider:
import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.AsyncTaskLoader;
/**
* Used to write apps that run on platforms prior to Android 3.0. When running
* on Android 3.0 or above, this implementation is still used; it does not try
* to switch to the framework's implementation. See the framework SDK
* documentation for a class overview.
*
* This was based on the CursorLoader class
*/
public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {
private Cursor mCursor;
public SimpleCursorLoader(Context context) {
super(context);
}
/* Runs on a worker thread */
@Override
public abstract Cursor loadInBackground();
/* Runs on the UI thread */
@Override
public void deliverResult(Cursor cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
/**
* Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
* will be called on the UI thread. If a previous load has been completed and is still valid
* the result may be passed to the callbacks immediately.
* <p/>
* Must be called from the UI thread
*/
@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
/**
* Must be called from the UI thread
*/
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
@Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
}
It only needs the AsyncTaskLoader
class. Either the one in Android 3.0 or higher, or the one that comes with the compatibility package.
I also wrote a ListLoader
which is compatible with the LoadManager
and is used to retrieve a generic java.util.List
collection.
android - CursorLoader & SQLite without Content Provider
The two implementations you mention in your post both offer all of the benefits of the CursorLoader
except the ability to receive notifications when the underlying content changes.
I've been looking into this a lot recently and I can confidently tell you that the Android API currently does not provide a means of doing this with only a raw SQLiteDatabase
(it only provides the ContentResolver#notifyChange()
and Cursor#setNotificationUri()
methods, which are used to notify all Cursor
s registered under a certain notification Uri
).
That said, your options right now are to:
Implement an observer yourself that is capable of receiving notifications from the
SQLiteDatabase
when the content changes, and is somehow able to relay these notifications to all existingLoader
s in your application. I wrote a pretty extensive blog post on how to implementLoader
s that might come in handy if you wish to take on this challenge. Or...Use Mark Murphy's
LoaderEx
library and only make database modifications using theAsyncTask
operations his library provides. Note that the reason why his tasks refresh theLoader
is because they callonContentChanged
on theLoader
immediately after the insertion/update/delete is performed, effectively telling theLoader
that the content has changed and that it should refresh its data.Just use a
ContentProvider
with aCursorLoader
and you can use theContentResolver#notifyChange()
method to notify theCursorLoader
that a content change has occurred.
I'm trying to figure out a better solution, and I'll report back in the future if I ever find/implement one, but for now these will have to do.
Do I need a Content Provider with a Cursor Loader?
After researching, I found that a ContentProvider IS needed if you are using the built in CursorLoader.
As wsanville says, you can roll your own Loader so that it does not need a ContentProvider.
Do I need a Content Provider with a Cursor Loader?
After researching, I found that a ContentProvider IS needed if you are using the built in CursorLoader.
As wsanville says, you can roll your own Loader so that it does not need a ContentProvider.
Should I use a Cursor or a CursorLoader?
As the documentation states,
CursorLoader implements the Loader protocol in a standard way for
querying cursors, building on AsyncTaskLoader to perform the cursor
query on a background thread so that it does not block the
application's UI.
This is the biggest advantage of using Loaders, i.e. it is asynchronous. Some of the other important advantages are also mentioned here.
- They provide asynchronous loading of data.
- They monitor the source of their data and deliver new results when the content changes.
- They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data.
If you use the default cursors by querying the content provider directly then you need to handle closing them, and as you said you have huge data, you'd have to run the query code on a different thread. For all these purposes using CursorLoaders is much simpler and efficient. For code on how to use one, check this out.
As to your second question,
I also saw that I have to implement it in every class the
cursorLoader, can't I make a single class for it and call it when it's
needed ?
You can very well make a Base class that will be implementing the loader callbacks and then you can inherit that base class from all the classes that need to use the CursorLoaders.
How do I go about implementing CursorLoader without using a Listview?
Is it possible to extract the information from the returned adapter
There is no "returned adapter". CursorLoader
is a Loader
that returns a Cursor
. None of those things are "adapters" as the term is used in Android.
and update the TextViews inside the ViewGroups?
Yes. The Cursor
API allows you to set which row you want to work with (moveToPosition()
, etc.), from which you can retrieve columns (getString()
, etc.).
Do I need to re-write it to use a Loader?
CursorLoader
works only with a ContentProvider
. If your DatabaseHelper
(presumably a subclass of SQLiteOpenHelper
) is used by a ContentProvider
, then your CursorLoader
can work with that ContentProvider
. Other alternatives include:
not bothering with the
Loader
framework, and just usingAsyncTask
and kin for doing your database I/O in the backgroundusing my
SQLiteCursorLoader
, which works directly with aSQLiteOpenHelper
Do apps that won't share need Content Provider?
You can write a Loader
(or use CommonsWare
's SQLiteCursorLoader
) to query your SQLiteDatabase
directly instead. The documentation is correct in that you don't really need a ContentProvider
if your app only requires simple access to local data (as opposed to sharing that data with different processes/applications).
That said, the ContentProvider
does offer a few benefits. For example, you need one to implement a SyncAdapter
or a search interface with the SearchManager
. I try to incorporate these into my applications so I find myself implementing ContentProvider
s all the time. The ContentResolver
also provides an easy means of providing global notifications to the underlying data source when changes are made. For example, the CursorLoader
will register a ContentObserver
on its Cursor
, causing the Cursor
to receive a notification when you call ContentResolver#notifyChange(Uri uri, ContentObserver observer)
on the given Uri
. If you were to load data directly from your SQLiteDatabase
instead, setting this up would require a bit more work.
Related Topics
How to Develop a Soft Keyboard For Android
Android Studio Keeps Refusing to Resolve Com.Android.Support:Appcompat-V7:29.0.1
How to Make Wrap_Content Work on a Recyclerview
Android Background Music Service
How to Pass Arraylist<Customeobject> from One Activity to Another
Telephonymanager.Getline1Number() Failing
Disable Scrollview Programmatically
"No Cached Version... Available for Offline Mode."
How to Run a Method Every X Seconds
Android: How to Display Video Thumbnails
Android Youtube App Play Video Intent
Animate the Transition Between Fragments
Android Push Notifications: Icon Not Displaying in Notification, White Square Shown Instead
How to Read a Text File from the Sd Card in Android
How to Connect to More Than One Firebase Database from an Android App
Overriding the Home Button - How to Get Rid of the Choice