Reputation: 25
Hey, So after looking around It seems the consensus is that by using publishProgress() would be the way to update the UI for longer calculations. I seem to haave implemented this incorrectly though, because when I run this code I get an ANR. Can you help me understand why?
Here is my code: http://pastebin.com/zQNhkDJ9
Upvotes: 0
Views: 743
Reputation: 32748
You need to make your AsyncTask a static inner class and it has a property which is a reference to your Activity. Then you can just call methods on your Activity directly - like you for UIupdate().
An issue you have to be aware of is that you now have to be VERY careful to clear out this reference to your Activity in any onPause callbacks, otherwise you've set a circular reference and this will cause memory leaks.
My pattern is to have another basic Java object, usually another private static class in the Activity which I call "StateHolder" and its my gateway for handling the AsyncTask stuff, such as initializing it and clearing it out (setting to NULL) and cancelling any tasks in progress during rotation changes. In your callback for "onRetainNonConfigurationInstance" you can pass use your StateHolder as your marshalled object.
Here is some rough code:
private static class GetConnectionsStatusTask extends AsyncTask { private BoothActivity mActivity; private Exception mReason;
public GetConnectionsStatusTask(BoothActivity activity) {
super();
mActivity = activity;
}
public void setActivity(BoothActivity activity) {
mActivity = activity;
}
@Override
protected void onPreExecute() {
if(mActivity != null) {
mActivity.startProgressBar("Loading", "Please wait");
}
}
@Override
protected Connections doInBackground(Void... values) {
try {
App app = (App)mActivity.getApplication();
return(app.getApp().userConnections());
}catch(Exception e) {
mReason = e;
return(null);
}
}
@Override
protected void onPostExecute(Connections status) {
super.onPostExecute(status);
if(mActivity != null) {
mActivity.completedConnectionsStatusCheck(status, mReason);
}
}
}
private static class StateHolder { private GetConnectionsStatusTask connectionsTask;
public void cancelTasks() {
if (connectionsTask != null) {
connectionsTask.setActivity(null);
connectionsTask.cancel(true);
connectionsTask = null;
}
}
public void setActivityForTasks(BoothActivity activity) {
if (connectionsTask != null) {
connectionsTask.setActivity(activity);
}
}
public void startConnections(BoothActivity activity) {
if(mIsConnectionsChecking == false) {
mIsConnectionsChecking = true;
connectionsTask = new GetConnectionsStatusTask(activity);
connectionsTask.execute();
}
}
}
Then in onPause I have code like:
if(mStateHolder != null) {
mStateHolder.cancelTasks();
}
And in onCreate you can re-inflate your StateHolder like so:
Object retained = getLastNonConfigurationInstance();
if(retained != null && retained instanceof StateHolder) {
mStateHolder = (StateHolder) retained;
mStateHolder.setActivityForTasks(this);
} else {
mStateHolder = new StateHolder();
}
This will also re-initialize your StateHolder with the current activity so you can re-establish any UI widgets, such as progress bar updates, etc.
Using this pattern you can actually have running async tasks and handle rotation changes with progress bars re-initializing themselves.
Upvotes: 0
Reputation: 1197
Your problem is probably that your doInBackground function is just calling publishProgress. All of your work is being done in publishProgress, which runs on the UI thread, so you'll still get an ANR that way. You need to do your heavy lifting inside doInBackground, and then once your processing is done you can update the UI thread.
Upvotes: 1