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.
Related
My AsyncTask for fetching data is a long one, and at the same time, in the midst of constructing my recyclerview I wanted to check if a cursorloader was able to query from my content provider correctly.
Bear with me here, I used Loader Callbacks interface and onLoadFinished to get results. Since the asynctask is still running, the loader still calls the content provider as it gets updated until asynctask ends. I would except that since onLoadFinished primarily deals with filling in the contents of an adapter with cursor data that I shouldn't worry that it prints out log statements simultaneously as the asynctask continues to run, but I wanted to confirm.
I do intend to eventually move this asynctask into an intentservice that only gets called via broadcast.
You can call getLoaderManager().restartLoader(URL_LOADER, null, HomeFragment.this); to restart the loader on you AsyncTask onPostExecute method. You need to init the Loader first on you onCreate() method.
This way, the loader will refresh with the same projection it had when you started it and it will fetch the results of the AsyncTask operations from DB.
I have a CursorLoader that provides data to a ListFragment. I have the notification mechanism correctly configured in my ContentProvider with:
cursor.setNotificationUri(getContext().getContentResolver(), uri);
and
getContext().getContentResolver().notifyChange(uri, null)
When I select a list item, a new child Activity is opened and in the onCreate() of this Activity I do a query modifying the item details.
I know the notification is fired and the CursorLoader re-queries the content provider.
The problem I'm facing is that sometimes my CursorLoader is cancelled because the parent Activity is being paused (or stopped) and the onLoadFinished() never gets called.
When the CursorLoader is cancelled, even if I re-init the loader onResume() of the ListFragment, I can't see any change in the data:
#Override
public void onResume() {
super.onResume();
getLoaderManager().initLoader(URL_LOADER, null, this);
}
I'm using support library v4.
I'm assuming CursorLoader updates the parent Activity even when it is on background. Do I need to restart the loader when the ListFragment resumes?
(edit)
I forgot to mention that I'm creating and starting the loader from a ListFragment.
After some debugging, I found that when the ListFragment is stopped, the loader manager cancels all current loaders for the fragment.
In my case, when the new child activity is created, a db change triggers a notification that makes the loader perform a re-query. If the query does not finish before the parent fragment stops, the loader is canceled and the old cursor is still valid. When I navigate back to this parent activity with the ListFragment from the child activity, the data shown is from the old cursor. In this case, the notification is lost and I have to restart the loader onResume().
I did a test where I modified the db only after 10s since the child activity started, and because the loader was not canceled (because the parent activity was already stopped), I received a new cursor when returning to the parent activity.
Can anybody help me find a solution that does not involve a forced re-query (restartCursor()) every time the parent activity resumes?
When using the support library, you need to use getSupportLoaderManager() instead of getLoaderManager(), But you can only get it from the FragmentActivity. So try using this code instead:
getActivity().getSupportLoaderManager().initLoader(id, null, this);
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
Do i have to close a cursor when using
startManagingCursor(cursor); //I know its deprecated
and
cursor.registerContentObserver(..
if so, where to close it?
If using startManagingCursor you do not need to close the cursor, Activity will take care of that for you.
But if you have called registerContentObserver , you need to call unregisterContentObserver. Usually you call registerContentObserver in OnResume and unregisterContentObserver in OnPause, But it can change depending upon what kind of operations you are performing in ContentObserver.
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