I've got an Android activity which grabs an RSS feed from a URL, and uses the SAX parser to stick each item from the XML into an array. This all works fine but, as expected, takes a bit of time, so I want to use AsyncActivity to do it in the background. My code is as follows:
class AddTask extends AsyncTask<Void, Item, Void> {
protected void onPreExecute() {
pDialog = ProgressDialog.show(MyActivity.this,"Please wait...", "Retrieving data ...", true);
}
protected Void doInBackground(Void... unused) {
items = parser.getItems();
for (Item it : items) {
publishProgress(it);
}
return(null);
}
protected void onProgressUpdate(Item... item) {
adapter.add(item[0]);
}
protected void onPostExecute(Void unused) {
pDialog.dismiss();
}
}
Which I call in onCreate() with
new AddTask().execute();
The line items = parser.getItems() works fine - items being the arraylist containing each item from the XML. The problem I'm facing is that on starting the activity, the ProgressDialog which i create in onPreExecute() isn't displayed until after the doInBackground() method has finished. i.e. I get a black screen, a long pause, then a completely populated list with the items in. Why is this happening? Why isn't the UI drawing, the ProgressDialog showing, the parser getting the items and incrementally adding them to the list, then the ProgressDialog dismissing?
I suspect something is blocking your UI thread after you execute the task. For example, I have seen folks do things like this:
MyTask myTask = new MyTask();
TaskParams params = new TaskParams();
myTask.execute(params);
myTask.get(5000, TimeUnit.MILLISECONDS);
The get invocation here is going to block the UI thread (which presumably is spinning off the task here...) which will prevent any UI related stuff in your task's onPreExecute() method until the task actually completes. Whoops! Hope this helps.
This works for me
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(viewContacts.this);
dialog.setMessage(getString(R.string.please_wait_while_loading));
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
}
It is because you used AsyncTask.get() that blocks the UI thread "Waits if necessary for the computation to complete, and then retrieves its result.".
The right way to do it is to pass Activity instance to your AsyncTask by constructor, and finish whatever you want to do in AsyncTask.onPostExecution().
If you subclass the AsyncTask in your actual Activity, you can use the onPostExecute method to assign the result of the background work to a member of your calling class.
The result is passed as a parameter in this method, if specified as the third generic type.
This way, your UI Thread won't be blocked as mentioned above. You have to take care of any subsequent usage of the result outside the subclass though, as the background thread could still be running and your member wouldn't have the new value.
Related
I am trying to update my list and display the updated list in my activity. Every time I do so I get the following error
The content of the adapter has changed but ListView did not receive a notification.
Make sure the content of your adapter is not modified from a background thread,
but only from the UI thread.
Here is my code, I am using an AsyncTask to achieve this task. I call notifyDataSetChanged() on my adapter but it does not seem to work. What am I doing wrong?
class MyTask extends AsyncTask<Context, Integer, String> {
#Override
protected String doInBackground(Context... params) {
GregorianCalendar currentDateclone = (GregorianCalendar) currentDate.clone();
ListPopulater2.listPopulate(currentDateclone, 7, items, Id);
//method to update list info
}
// -- gets called just before thread begins
#Override
protected void onPreExecute() {
progressdialog = ProgressDialog.show(SectionListExampleActivity.this, "", "Loading..."); //display progress bar
super.onPreExecute();
}
// -- called as soon as doInBackground method completes
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Log.d("postExecute", "here");
adapter.notifyDataSetChanged();
progressdialog.cancel();
}
}
Try moving:
ListPopulater2.listPopulate(currentDateclone, 7, items, Id);
to your onPostExecute.
The reason that this worked is because you need to update the list after you've completed all your background activity, not while you are doing it. You can use the onProgressUpdate, but that is for your progress dialog. Updating you list view should be done after you have all you data, otherwise, you will get the error you did because the UI runs on the main thread, and you are trying to update it with the background thread.
As for the progress dialog, there is a purpose. If you are doing something that is going to take awhile to complete, the dialog will tell the user how close you are to completing the background task.
Hope that makes sense.
When Ever you want to do something which deal with the UI you have to do that task in UI thread.
So for this you can use 2 approach:
use runOnUi() method
Use OnPostExecute() method
And you have to use notifyDatasetchanged() at that place where you are setting adapter in listView.
I have a Main activity with a few methods.
GetTopics: calls REST API and returns JSON results
BindTopics: displays the results from GetTopics on the screen
There are a few others with similar functionality, but I'm sure I can fit the solution to these across all methods.
Question: What is the proper way to run these in separate threads? Should both of them be in the same thread? Should I call BindTopics once GetTopics is complete?
Either way, doesn't the UI get built prior to the async threads are completed? If so, how do I update the UI?
Any help is appreciated.
The answer to all your questions is AsyncTask
I would load it in a progress dialog with an AsyncTask. You will need to declare the handler to update controls in the UI thread.
Example:
private Handler handler = new Handler();
final ProgressDialog pd = new ProgressDialog(this);
pd.setTitle("Getting topics..");
pd.setMessage("Please while topics are retrieved");
pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pd.setIndeterminate(true);
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
GetTopics();
return null;
}
#Override
protected void onPreExecute() {
pd.show();
super.onPreExecute();
}
#Override
protected void onPostExecute(Void result) {
BindTopics();
pd.dismiss();
handler.post(new Runnable() {
public void run() {
// update UI
// remove loading view
// load details of topics
}
});
super.onPostExecute(result);
}
}.execute();
You want to create your UI (with some sort of loading screen), then start an AsyncTask to download the data and display it.
There is a basic blog post about AsyncTasks here: http://android-developers.blogspot.co.uk/2009/05/painless-threading.html
In the AsyncTasks doInBackground method you would call GetTopics. Then in the onPostExecute method you would call BindTopics and display the data. In the onPostExecute method you can check to see if the data downloaded properly before attempting to display it.
The main use of threads for me were when my app had to download large amount of data from ftp server. But while downloading I wanted that the user still can use my app. If I had written the download code in main thread, the user could not use the app and all buttons would not work. The main idea of thread is to divide time of CPU usage for each thread.
If you wish more then one task to be preformed simultaneously you have to use Threads.
While I'm updating my database I want to display a progress dialog. My problem is that the ProgressDialog is getting late to appear,after 4-5 seconds, then appears and disappears very fast, it stays on screen few milliseconds almost you can't see it, then new data are shown in the list immediately. This makes me think that the ProgressDialog is waiting for database to be updated(it doesn't take much, about 4,5 seconds) and then it shows on the screen but is dismissing very fast. I would like the ProgressDialog appear immediately I press the 'Update' button and stay on the screen about 4-5 seconds.
class MyAsyncTask extends AsyncTask<Void, Void, Void>{
ProgressDialog myprogsdial;
#Override
protected void onPreExecute(){
myprogsdial = ProgressDialog.show(MyActivity.this, null, "Upgrade", true);
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
RefreshDataBase();
}
});
return null;
}
#Override
protected void onPostExecute(Void result){
myprogsdial.dismiss();
}
}
When I call it, new MyAsyncTask().execute();
Ok I think that this
runOnUiThread(new Runnable() {
is causing this behavior.
doInBackground() executes your code in a new thread to the main UI thread. You are then putting the code to execute in this thread back into the main one causing the progress dialog to be delayed at the end and then in postExecute() it gets closed immediately.
A good asyntask tutorial can be found here.
You must not use runOnUiThread. What you're basically did is:
Started new non-ui thread
From this new non-ui thread you posted a long running task to UI thread.
Exited from non-ui thread.
Your ui thread now executes long-running operation (RefreshDataBase) and blocks the UI.
You should call RefreshDataBase() directly. And if this method touches UI, you have to refactor it.
I have solved it, using this answer of Vladimir Ivanov.
I have separated the functionality by the appearance.
I have kept the functionality(downloading new data) in doInBackground() and in onPostExecute() I updated the list: get the new adapter,called setListAdaper() and notifyDataSetChanged.
Of course, I quit using runOnUiThread(). Thanks to all for hints.
I have a ListActivity which launches another Activity based on the list selection. This second Activity needs to load a fair bit of data from the internet and as such there is a noticeable delay between when the user clicks on an item and when the Activity displays.
This is a problem because I currently have no way to indicate to the user that their click is being processed (even just changing the colour of the selected list item would be sufficient but I can't find a good way to do that). Ideally I'd be able to display an indeterminate ProgressDialog while the second Activity is loading.
I've tried a few different approaches for this but nothing seems to work as desired.
I've tried the following:
Retrieving the serializable data (not all of it but some part) in an AsyncTask in the first Activity and passing it as an extra to the second. This didn't really work well as a ProgressDialog I created in onPreExecute() didn't display immediately (it seems delayed by the processing done in doInBackground() for some reason.)
Here is the code for that:
AsyncTask<String, Void, String> read = new AsyncTask<String, Void, String>() {
Dialog progress;
#Override
protected void onPreExecute() {
progress = ProgressDialog.show(SearchActivity.this,
"Loading data", "Please wait...");
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
DatasetReader reader = new DatasetReader();
reader.setFundID(params[0]);
reader.addDatsets(FundProfile.datasets);
reader.populate();
return reader.toString();
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
progress.dismiss();
}
};
read.execute(selectedItem.getUniqueID());
try {
action = new Intent(SearchActivity.this, FundProfile.class);
action.putExtra("data", read.get());
} catch(Exception ex) {
ex.printStackTrace();
}
In the second Activity's onCreate() method (this does not work at all):
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarVisibility(true);
Here is the onCreate() method for the second approach:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setTitleColor(Color.WHITE);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarVisibility(true);
try {
setContentView(R.layout.fund_profile);
// init some data
setProgressBarVisibility(false);
} catch(Exception ex) {
FundProfile.this.finish();
}
}
If you have long operations you should not be doing them in onCreate in any case as this will freeze the UI (whether or not the activity is displayed). The UI set by onCreate will not appear and the UI will be unresponsive until after the onCreate call finishes.
It seems you can start your second activity and display a progress bar (or requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);), then start an ASyncTask which will be responsible for updating your UI once data has been retrieved.
Adam,
It sounds like you are looking for the Indeterminate Progress bar: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/ProgressBar2.html
You can display this while you are loading your second Activity then set the visibility to false once the second Activity has loaded its data.
Move creating the Intent -- and really anything you need to do after the AsyncTask completes -- into onPostExecute:
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
progress.dismiss();
Intent action = new Intent(SearchActivity.this, FundProfile.class);
action.putExtra("data", result);
// ... do more here
}
The problem is that AsyncTask.get() blocks until the task is completed. So in the code above, the UI thread is blocked and the ProgressDialog is never given a chance to appear until the task completes.
In my app I am doing some intense work in AsyncTask as suggested by Android tutorials and showing a ProgressDialog in my main my activity:
dialog = ProgressDialog.show(MyActivity.this, "title", "text");
new MyTask().execute(request);
where then later in MyTask I post results back to activity:
class MyTask extends AsyncTask<Request, Void, Result> {
#Override protected Result doInBackground(Request... params) {
// do some intense work here and return result
}
#Override protected void onPostExecute(Result res) {
postResult(res);
}
}
and on result posting, in main activity I hide the dialog:
protected void postResult( Result res ) {
dialog.dismiss();
// do something more here with result...
}
So everything is working fine here, but I would like to somehow to update the progress dialog to able to show the user some real progress instead just of dummy "Please wait..." message. Can I somehow access the progress dialog from MyTask.doInBackground, where all work is done?
As I understand it is running as separate Thread, so I cannot "talk" to main activity from there and that is why I use onPostExecute to push the result back to it. But the problem is that onPostExecute is called only when all work is already done and I would like to update progress the dialog in the middle of doing something.
Any tips how to do this?
AsyncTask has method onProgressUpdate(Integer...) that you can call each iteration for example or each time a progress is done during doInBackground() by calling publishProgress().
Refer to the docs for more details
you can update from AsyncTask's method onProgressUpdate(YOUR_PROGRESS) that can be invoked from doInBackground method by calling publishProgress(YOUR_PROGRESS)
the data type of YOUR_PROGRESS can be defined from AsyncTask<Int, YOUR_PROGRESS_DATA_TYPE, Long>