How can I edit the views of an activity from an AsyncTask BEFORE the task is complete? I cannot access any of the views from the doInBackground method, and I do not want to wait until the task has completed. Is there a way to go about doing this? Or do I have to create a thread within the doInBackground (which sounds to me would be bad practice) and then access the view on that thread's completion?
Override this method:
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
Call it in doInBackground() of your AsyncTask. This method publishProgress(Progress... values)
happens on the UI thread when you call it from doInBackground().
For more information, read AsyncTask
You could define the class MyAsyncTask containing the following:
private final ProgressDialog progressDialog;
public MyAsyncTask(Context context) {
super(context);
progressDialog = new ProgressDialog(context);
progressDialog.setTitle(R.string.title);
progressDialog.setButton(
DialogInterface.BUTTON_NEGATIVE,
context.getResources().getString(R.string.abort),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
MyAsyncTask.this.cancel(true);
progressDialog.dismiss();
}
}
);
progressDialog.show();
}
#Override
protected void onProgressUpdate(String... params) {
progressDialog.setMessage(params[0]);
}
You can then use the ProgressDialog to report back to the GUI, e.g., by calling publishProgress("TEST").
You can update the UI from onProgressUpdate(). There is a tricky way, you can send some integer or boolean value to progress update and change your UI according to the condition.
#Override
protected void onProgressUpdate(Integer... values) {
if(values[0] == 1) {
// update the UI
button.settext("updating");
}
}
You can call onProgressUpdate fram asynctask by calling
publishProgress(1);
Hope this works, try out.
You need to override this method inside your AsynTask Class:
#Override
protected void onProgressUpdate(Void... values) {
// TODO Auto-generated method stub
ProgressBar bar = (ProgressBar) findViewById(R.id.prgLoading);
bar.setIndeterminate(true);
super.onProgressUpdate(values);
}
You can define a progress bar as I show in here and set some value into it.
Related
I use an async task to upload an image and get some results.
While uploading the image I see a progress dialog, written in onPreExecute() method like this:
protected void onPreExecute() {
uploadingDialog = new ProgressDialog(MyActivity.this);
uploadingDialog.setMessage("uploading");
uploadingDialog.setCancelable(true);
uploadingDialog.show();
}
Ok when I press the back button, obviously the dialog disappears because of the setCancelable(true).
But (obviously) the async task doesn't stop.
So how can I fix this? I want to cancel both dialog and async task when I press the back button. Any ideas?
From SDK:
Cancelling a task
A task can be cancelled at any time by invoking cancel(boolean).
Invoking this method will cause subsequent calls to isCancelled()
to return true.
After invoking this method, onCancelled(Object), instead of
onPostExecute(Object) will be invoked after doInBackground(Object[]) returns.
To ensure that a task is cancelled as quickly as possible,
you should always check the return value of isCancelled() periodically from
doInBackground(Object[]), if possible (inside a loop for instance.)
So your code is right for dialog listener:
uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
myTask.cancel(true);
//finish();
}
});
Now, as I have mentioned earlier from SDK, you have to check whether the task is cancelled or not, for that you have to check isCancelled() inside the onPreExecute() method.
For example:
if (isCancelled())
break;
else
{
// do your work here
}
FOUND THE SOLUTION:
I added an action listener before uploadingDialog.show() like this:
uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
public void onCancel(DialogInterface dialog) {
myTask.cancel(true);
//finish();
}
});
That way when I press the back button, the above OnCancelListener cancels both dialog and task. Also you can add finish() if you want to finish the whole activity on back pressed. Remember to declare your async task as a variable like this:
MyAsyncTask myTask=null;
and execute your async task like this:
myTask = new MyAsyncTask();
myTask.execute();
I spent a while figuring this out, all I wanted was a simple example of how to do it, so I thought I'd post how I did it. This is some code that updates a library and has a progress dialog showing how many books have been updated and cancels when a user dismisses the dialog:
private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
private ProgressDialog dialog = new ProgressDialog(Library.this);
private int total = Library.instance.appState.getAvailableText().length;
private int count = 0;
//Used as handler to cancel task if back button is pressed
private AsyncTask<Void, Integer, Boolean> updateTask = null;
#Override
protected void onPreExecute(){
updateTask = this;
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setOnDismissListener(new OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
updateTask.cancel(true);
}
});
dialog.setMessage("Updating Library...");
dialog.setMax(total);
dialog.show();
}
#Override
protected Boolean doInBackground(Void... arg0) {
for (int i = 0; i < appState.getAvailableText().length;i++){
if(isCancelled()){
break;
}
//Do your updating stuff here
}
}
#Override
protected void onProgressUpdate(Integer... progress){
count += progress[0];
dialog.setProgress(count);
}
#Override
protected void onPostExecute(Boolean finished){
dialog.dismiss();
if (finished)
DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
else
DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
}
}
create some member variables in your activity like
YourAsyncTask mTask;
Dialog mDialog;
use these for your dialog and task;
in onPause() simply call
if(mTask!=null) mTask.cancel();
if(mDialog!=null) mDialog.dismiss();
I would like to improve the code. When you canel the aSyncTask the onCancelled() (callback method of aSyncTask) gets automatically called, and there you can hide your progressBarDialog.
You can include this code as well:
public class information extends AsyncTask<String, String, String>
{
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... arg0) {
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
this.cancel(true);
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onCancelled() {
Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
dialog.hide(); /*hide the progressbar dialog here...*/
super.onCancelled();
}
}
Most of the time that I use AsyncTask my business logic is on a separated business class instead of being on the UI. In that case, I couldn't have a loop at doInBackground(). An example would be a synchronization process that consumes services and persist data one after another.
I end up handing on my task to the business object so it can handle cancelation. My setup is like this:
public abstract class MyActivity extends Activity {
private Task mTask;
private Business mBusiness;
public void startTask() {
if (mTask != null) {
mTask.cancel(true);
}
mTask = new mTask();
mTask.execute();
}
}
protected class Task extends AsyncTask<Void, Void, Boolean> {
#Override
protected void onCancelled() {
super.onCancelled();
mTask.cancel(true);
// ask if user wants to try again
}
#Override
protected Boolean doInBackground(Void... params) {
return mBusiness.synchronize(this);
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
mTask = null;
if (result) {
// done!
}
else {
// ask if user wants to try again
}
}
}
public class Business {
public boolean synchronize(AsyncTask<?, ?, ?> task) {
boolean response = false;
response = loadStuff(task);
if (response)
response = loadMoreStuff(task);
return response;
}
private boolean loadStuff(AsyncTask<?, ?, ?> task) {
if (task != null && task.isCancelled()) return false;
// load stuff
return true;
}
}
I had a similar problem - essentially I was getting a NPE in an async task after the user had destroyed the activity. After researching the problem on Stack Overflow, I adopted the following solution:
volatile boolean running;
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
running=true;
...
}
public void onDestroy() {
super.onDestroy();
running=false;
...
}
Then, I check "if running" periodically in my async code. I have stress tested this and I am now unable to "break" my activity. This works perfectly and has the advantage of being simpler than some of the solutions I have seen on SO.
You can just ask for cancellation but not really terminate it. See this answer.
How to cancel AsyncTask
Full answer is here - Android AsyncTask Example
AsyncTask provides a better cancellation strategy, to terminate currently running task.
cancel(boolean mayInterruptIfitRunning)
myTask.cancel(false)- It makes isCancelled returns true. Helps to cancel the task.
myTask.cancel(true) – It also makes isCancelled() returns true, interrupt the background thread and relieves resources .
It is considered as an arrogant way, If there is any thread.sleep() method performing in background thread, cancel(true) will interrupt background thread at that time. But cancel(false) will wait for it and cancel task when that method completes.
If you invoke cancel() and doInBackground() hasn’t begun execute yet. onCancelled() will invoke.
After invoking cancel(…) you should check value returned by isCancelled() on doInbackground() periodically. just like shown below.
protected Object doInBackground(Params… params) {
while (condition)
{
...
if (isCancelled())
break;
}
return null;
}
During android app onCreate I have a bunch of code that can be slow on some older devices. I want to have a progressdialog display while all the init code runs. Dialog won't show if called from onCreate because further code blocks the UI. So it needs to go into an AsyncTask.
This is my task.
class startupTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
pd = new ProgressDialog(mainContext);
pd.setMessage("Starting up. Please wait...");
pd.setIndeterminate(true);
pd.setCancelable(false);
pd.show();
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
pd.show();
runInit();
}
});
return null;
}
#Override
protected void onPostExecute(Void result) {
if (pd != null) {
pd.dismiss();
}
super.onPostExecute(result);
}
}
But the dialog still does not show? runInit is the usual slow startup code. It needs to run on the UI.
If I remove the pd.dismiss call the progress dialog does appear once the init codeis finished, so it is being created OK, just not displaying.
According to other posts the above code is the right way to do it, but when called from onCreate it does not seem to work?
Any ways to force a dialog to display after a call to show? Any way I can get the dialog to show and wait until it is visible before continuing?
Thanks for any help.
EDIT: In the end I just had to bite the bullet and reorganise code so that only the slow non UI code was inside the AsyncTask. This got it working.
What's your runInit is doing? You're running on the UI thread, it's being blocked the same way.
onPreExecute(), onPostExecute() and onProgressUpdate() have access to the UI Thread, but doInBackground() doesn't.
if runInit() does something on the UI it's being blocked and may be generating an exception that you're ignoring.
you should do operations on doInBackground and use publishProgress() to send to onProgressUpdate() data that you want to update to your UI / ProgressBar Status.
here's an example:
new AsyncTask<Void,Object,Void>()
{
#Override
protected Void doInBackground(Void... params)
{
Object[] _progress = new Object[]{"Decompressing", 1};
publishProgress(_progress);
}
#Override
protected void onPostExecute(Void params)
{
}
#Override
protected void onPreExecute()
{
}
#Override
protected void onProgressUpdate(Object... values)
{
txtProgress.setText(values[0].toString());
progressBar.setProgress(Integer.parseInt(values[1].toString()));
}
}.execute();
Error: Getting error with UI thread..
when I'm trying to show an alert inside the doInBackground. Is there any posibility do this stuff?
private class LoggingTask extends AsyncTask<String, Void, Integer> {
#Override
protected Integer doInBackground(String... params) {
//Get the sever connection and return the status
if(status==0){
// show an alert here....
}
return status;
}
#Override
protected Integer doInBackground(String... params) {
// Get the sever connection and return the status
publishProgress()
#Override
protected void onProgressUpdate(Void... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
//Do what ever you want
}
Do in background method doesnot allowed for UI changes,if you want to make any UI changes do in onpostexecute method
Do this way
#Override
protected Integer doInBackground(String... params) {
// Get the sever connection and return the status
if (status == 0) {
new Handler().post(new Runnable() {
#Override
public void run() {
// show an alert here....
}
});
}
return status;
}
Make a method that creates the dialog in your activty ( context would be the activity ) not in the AsyncTask. So when your
if ( status == 0 )
returns true, you call the method from the UI thread. In this way you are not creating the dialog in the AsyncTask and it should work.
doInBackground() is not the right place to do the UI related Task. You have to do it in onPostExecute. Or you can can start the dialog in OnPreExecute() and than cancel the dialog onPostExecute().
Async tasks don't run on the ui thread that's why you can't do that. Store a boolean. Then call the alert dialog after the asynchronous task completes in the onpostexecute method.
I am running an async task.
In the doinbackground method i have a method that returns int or some other value may be boolean value some times.
I also want to increase my progress bar as it goes to finishing.
problem is how to keep the track of counter ?
Some times db query may take some time and some times it goes faster . :)
public void getAllSchoolsSearchResult(InputBean nb , SearchLogic mLogic){
mSRLogic=mLogic;
new AsyncTask<Void, Integer, List<SResultModel>>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setProgress(0);
mProgressDialog.setMax(100);
mProgressDialog.show();
}
#Override
protected List<SResultModel> doInBackground(
Void... paramArrayOfParams) {
progressbar_Status=0;
while(progressbar_Status<100){
progressbar_Status += 1;
publishProgress(progressbar_Status);
}
if(ConnectionProvider.checkConnection()==false){
return null;
}
return SearchResultHandler.searchStudent(searchAllSchools);
}
#Override
protected void onPostExecute(List<SResultModel> result) {
super.onPostExecute(result);
if(mProgressDialog!=null&&mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mProgressDialog.setProgress(values[0]);
}
}.execute();
}
In the case of an unknown duration you should probably be using an indeterminate progress bar which you can set via the android:indeterminate layout property or setIndeterminate method.
If you know how many operations you're doing you can hold a member variable inside the asynctask or a local variable in the doInBackground method.
publishProgress can't be used like this.
From your code, you are showing the progress bar (while doing nothing) and then when it reaches 100% then you request the students.
The publishProgress should be inside the
SearchResultHandler.searchStudent()
depending on how it's implemented. If it doesn't have a loop inside, you won't be able to publish any progress.
In this cases, use an INDETERMINATE progress dialog, and do nothing in your doInBackground
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog.setIndeterminate(true);
mProgressDialog.show();
}
protected List<SResultModel> doInBackground(Void... paramArrayOfParams) {
if(!ConnectionProvider.checkConnection()) return null;
return SearchResultHandler.searchStudent(searchAllSchools);
}
Dear Android hackers,
I am trying to do the following in my Android App: When the User clicks on a list item in a ListActivity, a ProgressDialog should show up, some preloading should happen and after it's done, another Activity should be called using an intent.
I tried different approaches. What didn't work at all was using an Async Task. Apparently I cannot show, dismiss or edit my ProgressDialog out of the Async Task, if that Class is not a Member of my original Activity.
I switched to a simple Thread then, this is how I'm trying to do it:
dialog = ProgressDialog.show(BookmarkActivity.this, "", "Loading...",true);
new Thread() {
public void run() {
// do something
dialog.setMessage("Change Message...");
// do more
dialog.dismiss();
// ...
Intent intent = new Intent(BookmarkActivity.this, ThreadActivity.class);
BookmarkActivity.this.startActivity(intent);
}
}.start();
This works almost, but the changing of the dialog message does not. I'm getting errors saying something about "leaked windows". (I can post the complete log if it is needed).
My questions:
How can I use an Async Task for this, where the Class has it's own file?
How can I change the ProgressDialog out of my Thread or AsyncTask without causing an error for changing the UI in another thread?
Thanks in advance, Jan Oliver
Ok, with the help of Jason, I put together this Async Task. That works!
public class ThreadPreLoader extends AsyncTask<Object, String, Void> {
private Activity mActivity;
private ProgressDialog mDialog;
public ThreadPreLoader(Activity activity) {
mActivity = activity;
}
protected void onPreExecute() {
mDialog = new ProgressDialog(mActivity);
mDialog.setMessage("Loading...");
mDialog.show();
}
protected Void doInBackground(Object... args) {
publishProgress("Loading something else..");
return null;
}
protected void onProgressUpdate(String... msg) {
mDialog.setMessage(msg[0]);
}
protected void onPostExecute(Void result) {
mDialog.dismiss();
}
}
Thanks again, Jason.
You should use an Async Task, Define a custom Async Task which receives the context (this) of the original activity.
Then keep that context for later Dismissing the dialog.
From your doInBackground() method you can call postProgress( int progress) which will cause onProgressUpdate() to be called in the async task , this method is on the UI thread so it will not cause cross thread errors.
Once doInBackground() is complete the method onComplete() will also be called on the UI thread, this is where you can use your saved context and dissmiss the dialog (context.dissmissDialog()
Take a look at Android's Handler class. If you create the Handler in the onCreate method of your activity, Runnables that are sent to the post method of the handler are then run on the UI thread of your activity:
Handler h;
protected void onCreate(Bundle bundle) {
h = new Handler;
new Thread() {
public void run() {
// your run code
h.post(new Runnable() { /* change dialog here */ });
}
}.start();
}
I'm not sure that's the best option, but worth a try.
In AsyncTask
You should do you work which need time in doInBackground and calling intent like things, that you need to do after this task should be in onPostExecute
public class ThreadPreLoader extends AsyncTask<Object, String, Void> {
private Activity mActivity;
private ProgressDialog mDialog;
public ThreadPreLoader(Activity activity) {
mActivity = activity;
}
protected void onPreExecute() {
mDialog = new ProgressDialog(mActivity);
mDialog.setMessage("Loading...");
mDialog.show();
}
protected Void doInBackground(Object... args) {
//do more
publishProgress("Loading something and reached somewhere..");
//do more
publishProgress("Loading something and reached somewhere..");
//do more
return null;
}
protected void onProgressUpdate(String msg) {
mDialog.setMessage(msg);
}
protected void onPostExecute() {
Intent intent = new Intent(BookmarkActivity.this, ThreadActivity.class);
BookmarkActivity.this.startActivity(intent);
mDialog.dismiss();
}
}