AsyncTask and CursorLoader [Android] - android

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.

Related

android activity with list loaded from parse.com

Say I have main activity that contains a list (never mind if it is a ListView or RecyclerView). Each time the activity is created it has in its onResume method a query for data from parse. The thing is the parse query I use calls findInBackground method so my list in main activity is always zero since by the the time the activity is constructed the query hasn't finish yet. when I use find (and not find in background) I see in android studio logcat a sentence like the follwoing : "to many operation on main thread".
What do u think I should do?
Thank u in advance
Expansive operations like this should be done on a seperate thread in the background, so the main (animation) thread will not be blocked, causing your app to lag. The findInBackground() method will do the same as find(), but on a different thread 'in the background'. When the job is done, it will call the FindCallback and pass the result so you can update your list.
Untill this is done you could show a intermediate ProgressBar in front of your empty list to indicate that the data is loading.

What does AsyncTaskLoader.deliverResult() actually do?

I am trying to understand some finer points of AsyncTaskLoaders. This may be obvious, to others but I can't find an unambiguous example or definition that demonstrates and exmplains what happens when you override the deliverResult() method. What actually gets delivered ? How does this interact with the calling object ? I can see use of super.deliverResult, which passes a private object from the class. So, does the loader automatically know what to associate with the "delivered result". I am totally confused.
Seems I'm a bit late to the party, but anyway...
One of the main advantages of this intermediary step between the background loading and the UI thread's callback onLoadFinished() getting called
loadInBackground()
deliverResult() and
the callback onLoadFinished()
is that it gives us a means of shortcutting the whole loading process from within the AsyncTaskLoader class.
And this can be put to good use for caching the loading result within your AsyncTaskLoader and preventing the background loading from happening if there is cached data.
And why would we want to do this? Isn't the whole point of loaders dealing with those dreaded activity lifecycle issues (e.g. rotating the device), maintaining state (like, caching data) and having a means to get updated when underlying data changes (CursorLoader)?
Well, yes, but this isn't the whole story.
Consider this use case:
You've got your app (the one with the AsynTaskLoader) up-and-running and it already has loaded data into your UI.
Then, you switch over to your Twitter app to check on some news and return to you app.
Without caching, upon returning to your app, the loader would do its reloading.
This behavior is different from the one after configuration changes, e.g. rotating your device, in which case no reloading would take place.
So, how would we then prevent the loader from re-fetching data in case we're just sending our app to the background and, later, return to it again?
Solution
Create a cache member variable in your AsyncTaskLoader implementation.
Override deliverResult() so that you save your fetched data in your cache first, before you call the superclass's implementation of deliverResult().
In onStartLoading() check if there's cached data, and if so, let your AsyncTaskLoader just deliver that. Otherwise, start loading.
Here's a link to a sample app which implements this behaviour.
It's just a "Toy app" and as such part of Udacity's current version of the "Developing Android Apps" fundamentals course. And here is the link to the respective video within that course that deals with this issue. (The course is free, but you'll still have to sign-up w/ Udacity).
In short, what this app demonstrates, is a UI in which the user can input a search query for searching GitHub's repos (via the GitHub API), showing the resulting search URL in a TextView and also the raw JSON fetched from GitHub in another TextView.
The whole action happens in just MainActivity.java and the relevant part here is within the AsyncTaskLoader that's implemented as an anonymous inner class:
For step 1, just introduce a member variable in your AsyncTaskLoader implementation that's meant to serve as your data cache.
/* This String will contain the raw JSON
from the results of our Github search */
String mGithubJson;
For step 2, override deliverResult() as to cache the loading result.
When loadInBackground() has finished, it passes its return value to deliverResult().
It does so anyway, but now that we've overridden deliverResult() we can step right in and store our fetched data into the cache member variable which we've created with just so good foresight.
And finally, we chain up to the super class implementation of deliverResult() with super.deliverResult() which will pass-on the result to the callback method onLoadFinished(), running on the UI thread.
#Override
public void deliverResult(String githubJson) {
mGithubJson = githubJson;
super.deliverResult(githubJson);
}
For step 3, check in onStartLoading() whether or not we've got cached data.
If we don't have cached data (yet), just force the loading to begin with a call to forceLoad().
But if we do have cached data, just call deliverResult(yourCachedDataGoesHere) and pass-in the cached data as argument.
if (mGithubJson != null) {
deliverResult(mGithubJson);
} else {
forceLoad();
}
So, if you now switch back and forth between your app and some other app(s), you'll notice that no reloading takes place, as the loader will just use your cached data.
suppose when data are loading in the background, at this time, user press HOME button and exist the app, when user comes back to the app, loading has been finished. So we have already have the data, then AsyncTaskLoader will call the deliverResult() method, deliver the data to the onLoadFinished() method for displaying.
When the user come back to app, onStartLoading() is being called before loadInBackground(). In this method, we could check if our data if empty or not, if not empty, we call deliverResult() and send the result to onLoaderFinished(), so it could prevent to reload data.
When we press HOME exist the app and then come back, it will not create a new Loader, instead the old loader will try to load data.
The only answer I can find that makes any sense is based on a decription in this link.
"A registered listener to receive the Loader's results when it
completes a load. For each of its Loaders, the LoaderManager
registers an OnLoadCompleteListener which will forward the Loader’s
delivered results to the client with a call to
onLoadFinished(Loader loader, D result). Loaders should deliver
results to these registered listeners with a call to
Loader#deliverResult(D result)."
deliverResult appears to be used when you have listeners to the AsyncTask and want to send the results back to them. I would say it's uncommon. The Android documentation is even less descriptive:
"Sends the result of the load to the registered listener. Should only
be called by subclasses. Must be called from the process's main
thread.
Parameters
data : the result of the load"
deliverResult works after doInbackground completes. It sends the result D (returned by doInBackground) to the calling thread. You may wish to override it for cleaning data, but you can do clean-up in doInBackground instead without overriding deliverResult.

Single Use Loader

In the click handler for a button, I'm loading some data from a content provider (using getContentResolver().query(...)), then sending that data off in a network request. Since the query happens on the main thread with this approach, I want to move this off the main UI thread.
I think I can use a LoaderManager, and fire off the network request in onLoadFinished(), but the problem is that I don't want onLoadFinished() called ever again (for that Loader id), because I don't want to fire the network request again, during a screen orientation for example.
So, how do I use a LoaderManager for a query that I only want to happen only once?
Calling LoaderManager#initLoader() in your Activity#onCreate() method will either create a new Loader and force a new load, or reuse an existing Loader and deliver the most recently queried data if any exists. So as long as you are using the LoaderManager correctly (i.e. the way the developer's site recommends in the documentation), you shouldn't have any problems.
In your onLoadFinished(), you can call getLoaderManager().destroyLoader(loaderId) (or getSupportLoaderManager() as applicable). That will stop the Loader from automatically reloading. You may also need to ensure that where you are calling initLoader is guarded from executing again (via saving a boolean variable in onSaveInstanceState for example).

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.

Avoiding ANRs with Cursors

I have an activity that runs a query on a Sqlite DB, gets a Cursor, creates a CustomCursorAdapter with that Cursor, and attaches it to the ListView in the activity. It looks like this:
SQLiteDatabase db=new StuffHelper(this).getWritableDatabase();
Cursor c1=db.query(StuffHelper.TABLE,
new String[]{StuffHelper._ID},
StuffHelper.SIZE+">=?",
new String[]{"64"},
null,
null,
null);
startManagingCursor(c1);
StuffAdapter a1 = new StuffAdapter(this, c1);
ListView ll1 = (ListView) findViewById(R.id.ll1);
ll1.setAdapter(a1);
Is this current setup a problem in terms of ANR? When using cursors, how can I tell android to run all the Sqlite stuff on a background thread?
You didn't give much context of when this code is run, but I'll bite anyway...
Yes, it does run the risk of an ANR. It also runs the risk of various other lifecycle problems. Since setListAdapter() needs to be called before various other thing that you'd normally do in onCreate() you probably want to offload the database access to a separate thread (like an AsyncTask) that can be called/cached/managed as needed. AsyncTask gives you a UI-based callback before the thread starts and a UI-based callback when the thread ends. The ListAdapter can be created and assigned without any references to a Cursor (and I'd suggest you fix that asap... there doesn't seem to be a good reason why you're using a custom list adapter, you should be managing your database access better instead).
Managing this task over activity teardown and rebuilding (think changing orientation...) is an entirely different ball of wax and has been covered ad nauseam on SO.
Please separate your UI from background tasks. Write the cursor portion in background and in forground you can show any UI or progress dialog. Use AsyncTask for this
AsyncTask class has following methods
1. doInBackground: Code performing long running operation goes in this method. When onClick method is executed on click of button, it calls execute method which accepts parameters and automatically calls doInBackground method with the parameters passed.
2. onPostExecute: This method is called after doInBackground method completes processing. Result from doInBackground is passed to this method.
3. onPreExecute: This method is called before doInBackground method is called.
4. onProgressUpdate: This method is invoked by calling publishProgress anytime from doInBackground call this method.
How to use AsyncTask
Thanks
Deepak

Categories

Resources