Can you use a LoaderManager from a Service? - android

I have a data loading system set up using a custom Loader and Cursor that is working great from Activities and Fragments but there is no LoaderManager (that I can find) in Service. Does anyone know why LoaderManager was excluded from Service? If not is there a way around this?

Does anyone know why LoaderManager was excluded from Service?
As stated in the other answer, LoaderManager was explicitly designed to manage Loaders through the lifecycles of Acivities and Fragments. Since Services do not have these configuration changes to deal with, using a LoaderManager isn't necessary.
If not is there a way around this?
Yes, the trick is you don't need to use a LoaderManager, you can just work with your Loader directly, which will handle asynchronously loading your data and monitoring any underlying data changes for you, which is much better than querying your data manually.
First, create, register, and start loading your Loader when your Service is created.
#Override
public void onCreate() {
mCursorLoader = new CursorLoader(context, contentUri, projection, selection, selectionArgs, orderBy);
mCursorLoader.registerListener(LOADER_ID_NETWORK, this);
mCursorLoader.startLoading();
}
Next, implement OnLoadCompleteListener<Cursor> in your Service to handle load callbacks.
#Override
public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
// Bind data to UI, etc
}
Lastly, don't forget clean up your Loader when the Service is destroyed.
#Override
public void onDestroy() {
// Stop the cursor loader
if (mCursorLoader != null) {
mCursorLoader.unregisterListener(this);
mCursorLoader.cancelLoad();
mCursorLoader.stopLoading();
}
}

Unfortunately, no. Loaders were designed for activities and fragments in order to cleanly handle configuration changes that occur in Activites and Fragments. i.e. Rotating your device and re-attaching to the existing data.
A service does not have any configuration changes, it will sit in the background until it completes or the system is forced to kill it. So assuming you're executing your code on a background thread in your Service (which you should be anyways), theres just no reason to use a Loader. Simply make the calls you need to query your data.
So if your Service is just an IntentService, you can write your logic to query your cursor-backed data in the onHandleIntent() method.
http://developer.android.com/guide/components/loaders.html

Related

Loader restarted on Activity start confusion

I have a loader. I want it to only start when the underlying data model changes. Which I understood as the point of a loader. From the Android docs:
Loaders, in particular CursorLoader, are expected to retain their data
after being stopped. This allows applications to keep their data
across the activity or fragment's onStop() and onStart() methods, so
that when users return to an application, they don't have to wait for
the data to reload.
Great. However, whenever my activity resumes, my loader onStartLoading() is called. Debugging into the platform code, the implementation of Activity.onStart() ends up restarting all of the loaders. Specifically the call stack is,
Activity.onStart() -->
FragmentController..doLoaderStart() -->
FragmentHostCallback.doLoaderStart() -->
LoaderManagerImpl.doStart() -->
LoadermanagerImpl.LoaderInfo.start() -->
Loader.startLoader() -->
<my loader>.onStartLoading()
My loader is costly so I don't want it to reload when my activity is restarted, and this seems to go against the quote above which specifically states that loaders are supposed to retain their data across stops / start cycles.
Does this make sense?
The problem was basically a misunderstanding of loaders. When the framework calls a Loader's onStartLoading(), it's more like a lifecycle method. It's the framework telling your loader that the activity has started. Contrary to my original thinking, it doesn't mean that the loader must reload it's data.
A naive implementation of a loader (like mine) just reloads all of it's data on onStartLoading(). A smarter implementation only loads if the underlying data has changed. If we look at CursorLoader.onStartLoading(),
#Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
It first sends the cached result immediately if it has it. It then calls forceLoad() which actually loads the data, but it only does this if the data has changed.
The other detail is how it tracks when content has changed. It boils down to Loader's implementation of onContentChanged():
public void onContentChanged() {
if (mStarted) {
forceLoad();
} else {
// This loader has been stopped, so we don't want to load
// new data right now... but keep track of it changing to
// refresh later if we start again.
mContentChanged = true;
}
}
This method says: when the content has changed as we're started, load the data. Otherwise, just keep a flag letting us know the content has changed. It's basically delaying the actual load the of the data until the loader is started.
takeContentChanged() basically checks the mContentChanged instance field:
public boolean takeContentChanged() {
boolean res = mContentChanged;
mContentChanged = false;
mProcessingChange |= res;
return res;
}
As long as your loader implementation calls onContentChanged() when the content has changed, the implementation of Loader handles the rest.

What is the expected behavior of SQLite CRUD operations after a configuration change or if the activity that started those operations is destroyed?

I'm refactoring an app I made some time ago. Back then I was taking my first steps into Android and the easy way to go was to avoid orientation changes, for almost all my CRUD operations I used the AsyncTask class, didn't implement a Content Provider or used Fragments.
Now I have made some changes:
I use Fragments to encapsulate functionality
All accesses to my database are done through a Content Provider
I use CursorLoaders to retrieve my data taking advantage of the content observation and automatic background loading the LoaderFramework brings.
The only problem I have now is that I'm not quite sure how to handle the rest of the CRUD operations (Create,Update, and Delete).
I've found that I can use the AsyncQueryHandler class, but there's not enough information online.
One thing I like the Loader Framework, is that is aware of the Activity or Fragment lifecycle, but what about the AsyncQueryHandler?
What happens to the operations that were started with startDelete, startInsert, startUpdate when the Activity/Fragment undergoes a configuration change? Or when I press the back button? Or worse yet, if the activity is destroyed?
What is the expected behavior of this kind of operations in such cases? Should they be cancelled or should they continue to do their work?
All the operations I've mentioned above are not that complex. For real complex operations I've used Services or IntentServices, but since I don't consider a good idea to run SQLite operations on the main thread I want to use a better solution, but first I need to know how that solution should react to the Activity/Fragment lifecycle events.
Any comments or suggestions would me greatly appreciated.
Thanks.
If you use AsyncQueryHandler you have to take into consideration that this abstract wrapper is created and executes provider operations on the thread that it was started on. If you call this from UI thread the callbacks will be sent to the UI thread. If you call this from another working thread, the callbacks will be sent to that working thread.
This is not bound to the lifecycle of the fragment or the activity, ContentProviders don't have an lifecycle.
The AsyncQueryHandler at an basic level, creates an Message object which is added to the MessageQueue of an single background thread. No matter from which activity/fragment you use the AsyncQueryHandler all the Message objects will end up on the same MessageQueue.
Once the background thread processes an request it will send the response back to the instance of the AsyncQueryHandler from which the request was initially made.
My recommendation is to use the Loaders from Android. These are directly tied to the lifecycle of the activity/fragment. You can have multiple Loaders in a LoaderManager(one LoaderManager per activity/fragment) which allows to do more complex operations. Your activity/fragment will automatically be notified when the content has changed(very useful when you want to combine it with your custom methods for CRUD operations or if you need to use long running services). Another very important feature they have is that they will always reconnect to the last Loader, thus you will avoid re-querying your content.
I recommend you search for some tutorials for implementing Loaders in Android. You can start with these:
Loaders - part 1
Loaders - part 2
Loaders - part 3
Loaders - part 4
Answer for your last comments:
I suggest to use the EventBus library (https://github.com/greenrobot/EventBus) to make the communication between your AsyncTasks/Thread and your other components.
You can start by creating an abstract AsyncTask/Thread and on top of that to make your specific command.
For example:
public abstract class AbstractThread extends Thread {
#Override
public void run() {
super.run();
command();
}
abstract void command();
}
In the abstract class you could make some DB initialization, verification or anything else that might make sense for your application.
public class InserTask extends AbstractThread{
#Override
void command() {
//TODO: Add logic for the insert task
}
}
public class UpdateTask extends AbstractThread{
#Override
void command() {
//TODO: Add logic for the update task
}
}
In these specific classes, just add your logic of the CRUD operation.
To have control over these threads, like when they should be stopped, paused, resumed you could create and ThreadPool manager which controls your threads. You can read more how to achieve this here: Thread Pool

Delete item from listview and database

I'm trying to delete an item from a listview and from a sqlite database. There are several question on this site but I can't find a proper solution. My question is related with the design. I mean: the database access on Android should be done in async way to avoid to stop the main thread. My current listview, indeed, is loaded using an android Loader that it notified about any operation on the db and call onContentChanged(). The problem is how to proceed with the deletion: what is the best strategy? I see two ways:
Delete from adapter, call notifyDataSetChanged() on it and then delete from db with an AsyncTask (or similar). The Loader will be notified. In this case there is maybe a refresh problem because the adapter is updated again due to loader update.
Delete the item using an AsyncTask (or similar) and wait the update using the Loader. In this case the problem is that the UI is not updated when the user delete the item, but it's going to be deleted later (if no error occur) so it can be a bit weird from user point of view.
Both option will work, but different people will pick different way. The second way easier and more recommended as far as you leave all of the work to Loader, developed by Google.
The doc said it will refresh the data automatically and need to work just with callbacks as onLoadFinished and onLoadReset to complete it:
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);
}
And "clean" adapter when Loader was reset:
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);
}
Another words, I don't see any reason to pick first method, when second is making life easier.

CursorLoader does not refresh if notifyChange() is called before CursorLoader registers ContentObserver

I have a CursorLoader that loads data from local SQLite database.
The data in SQLite is actually a cached copy of a remote API response, so I start an asynchronous refresh of SQLite data whenever it is queried by the CursorLoader.
Following is the sequence of steps I follow to respond to the data query in my ContentProvider:
Query SQLite for cached data. cursor.setNotificationUri(getContext().getContentResolver(), uri) is called on the returned cursor.
Start an asynchronous call to remote API. Once the response is received, getContext().getContentResolver().notifyChange(uri, null) is called.
Sleep for 5 seconds to cause a delay in receipt of cursor by the CursorLoader. (This is done to easily reproduce my problem).
Return the cursor obtained in Step 1 to the CursorLoader.
In the less likely but possible scenario of the asynchronous remote API call completing (and notifyChange() being called) before the CursorLoader could register a ContentObserver on the obtained cursor by calling registerContentObserver(cursor, mObserver) [ see http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/content/CursorLoader.java#CursorLoader.loadInBackground%28%29 ], the data change notification is missed by the CursorLoader and therefore the Activity does not see updated data.
I can get around this problem by writing my LoaderCallBacks such that the data is loaded twice - first only cached data is loaded and then a refresh request is made. This way the CursorLoader gets to register ContentObserver before the refresh call starts.
However, this is something that should ideally be handled by the CursorLoader itself, as CursorLoader advertises automatic update of the cursor. Doing it in every Activity causes a lot of bloat in Activity code.
My approach is to register the observer before starting with the loading of the cursor. You need to register for the Uri instead of for the cursor.
getActivity().getContentResolver().registerContentObserver(
your_Uri, true,
new YourObserver(mHandler));
getLoaderManager().initLoader(0, null, this);
get a reference to your loader while initializing as follows
Loader dayWeatherLoader = getLoaderManager().initLoader(LOADER_DAY_WEATHER, null, this);
then create a class that extends ContentObserver as follows
class DataObserver extends ContentObserver {
public DataObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
dayWeatherLoader.forceLoad();
}
}
Then register content observer inside onResume lifecycle method as follows
#Override
public void onResume() {
super.onResume();
getContext().getContentResolver().registerContentObserver(CONTENTPROVIDERURI,true,new DayWeatherDataObserver(new Handler()));
}
Whenever there is a change in the underlying data of content provider, the onChange method of contentobserver will be called where you can ask loader to load the data again

Life Cycle / Flow control in content Provider?

Can somebody explain me the flow of control in this tutorial : http://www.vogella.de/articles/AndroidSQLite/article.html#tutorialusecp
I am not able to get the right flow. I am completely a novice to this content provider etc.
I wanted to know when does actually the DB gets created, what are the lifecycle methods and what is the sequence of method execution in this project ?
Finally found the Flow!! :
First of all, the onCreate of Content Provider is called just when the application launches as we have registered this in Manifest.
Then, onCreate of our first Activity, i.e. onCreate of ToDodOverviewActivity.
the call to fillData() has the initLoader() call which in turn calls the onCreateLoader of the Loader.
Then, here at
CursorLoader cursorLoader = new CursorLoader(this,
MyTodoContentProvider.CONTENT_URI, projection, null, null, null);
creation of Loader takes place. a loader that queries the ContentResolver and returns a Cursor. This class 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 in turn leads a call to onCreate() of DataHelper and ToDoTable etc and here everyone know the flow about SQLiteOpenHelper.
Finally, onLoadFinished() gets called which in turn swaps the cursor and updates the adapter.

Categories

Resources