I am facing the issue with displaying progressbar onItem selected in option menu.
My code is here:
case R.id.mnuLogout:
showDialog(Constants.PROGRESS_DIALOG);
closeOptionsMenu();
if(MyApp.IsLoggedOut())
handler.sendEmptyMessage(Constants.LOGOUT);
else
handler.sendEmptyMessage(Constants.ERROR_MSG);
Progressbar is displayed after completion of IsLogged method.
You're calling get() right after the AsyncTask as executed and lose asynchronous behavior because this method waits until task is finished. You should add all the code in try/catch block to AsyncTask.onPostExecute() method and also dismiss the dialog from this method.
void doLogout() {
new LogoutTask().execute();
}
void dispatchLogoutFinished() {
dismissDialog(Constants.PROGRESS_DIALOG);
if (MyApp.IsLoggedOut()) {
// do something
} else {
// do something else
}
}
private class LogoutTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
TheActivity.this.showDialog(Constants.PROGRESS_DIALOG);
}
protected Void doInBackground(Void... params) {
return null;
}
protected void onPostExecute(Long result) {
TheActivity.this.dispatchLogoutFinished();
}
}
And I don't think you need to send messages to the handler. The dispatchLogoutFinished() is executed on the UI thread, so there's no need for synchronization.
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;
}
I am using Asynctask to load all the contacts from the device. Although it has been discussed many times that Contacts.Contract is really slow and take me 10-15 secs to load all contacts data with image and all other data like email etc. So I have decided to start a service with asynctask on the splash screen.
Now the problem is
I have activity series A-B-C-D
Asynctask starts in Activity A and I want Contacts in Activity D. If all contacts is loaded till user reaches Activity D then its ok. But if user reaches Activity D and service is still running than I need to make user wait.
So my question is HOW TO WAIT FOR ASYNCTASK TO FINISH in Activity D. I will show a simple progress bar in activity D till Asynctask finishes. BUT HOW?
I have an idea to make async series in just one async task:
protected Boolean doInBackground(String... params) {
if(params[0] == "taskA") {
//do somthing
params[0] = "taskB";
}
if(params[0] == "taskB") {
//do somthing
params[0] = "taskC";
}
if(params[0] == "taskC") {
//do somthing
params[0] = "taskD";
}
if(params[0] == "taskD") {
//do somthing
return true;
}
And in your main thread just call async task like this:
ShowMyProgress();
new MyAsyncTask().execute("taskA");
And finally you can hide your progress on onPostExecute like:
protected void onPostExecute(final Boolean success) {
if (success) {
....
HideMyProgress();
}
}
To wait for AsyncTask to finish you can use get() method.
From documentation of get() method:
Waits if necessary for the computation to complete, and then retrieves
its result.
AsyncTask - get()
But this will make your main thread wait for the result of the AsyncTask.
Another way would be that you can show a progress dialog in the async task until it finishes. This way you can get the status calling:
task.getStatus() == Status.RUNNING
And if the status is Status.RUNNING you will just show the progress dialog until will be finished. So, as an example:
final AsyncTask task = MyAsyncTask.getInstance();
final Thread waitingThread = new Thread(new Runnable() {
#Override
public void run() {
try {
// ...
// show progress dialog
while(task.getStatus() != Status.FINISHED) {
TimeUnit.SECONDS.sleep(1);
}
result = task.get();
// ...
// hide progress dialog
} catch (InterruptedException e) {
// TODO log
}
}
});
waitingThread.start();
The task object is your asyncTask. You can make it Singleton and start in Activity A, and get instance of it in Activity D.
public class MyAsyncTask extends AsyncTask<Void, Void, Void>{
private static MyAsyncTask instance;
private MyAsyncTask(){}
public static MyAsyncTask getInstance() {
// check status if we want to execute it again
if (instance == null) {
instance = new MyAsyncTask();
} else if (instance.getStatus() == Status.FINISHED) {
instance = new MyAsyncTask();
}
return instance;
}
#Override
protected Void doInBackground(Void... params) {
// do smth
return null;
}
}
Haven't tried this in action but I think it will work.
you can do
myAsyn asyn = new myAsyn();
asyn.execute();
while(asyn.getStatus()!=Aynctask.Status.Finished)
{
Thread.sleep(100);
}
instead of waiting for asyntask to finish, you can simply show a progress dialog.
public class myAsyn extends AsyncTask<Void, Void, Void>{
private ProgressDialog dialog;
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
dialog = new ProgressDialog(context);
dialog.setMessage("Loading...");
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
#Override
protected Void doInBackground(String... arg0) {
// TODO Auto-generated method stub
//do your work
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
dialog.dismiss();
}
}
It is simple i will explain..
You can get the status of the async task task...like this
if (o2.getStatus() == Status.FINISHED) { ////o2 is a asynch task instance...
} else if (o2.getStatus() == Status.RUNNING) {
} else if (o2.getStatus() == Status.PENDING) {
}
AsyncTask has basically onPreExecute and doInbackground, and onPostExecute exists. The meaning of each!
onPreExecute:
Runs on the UI thread before doInBackground (Params. ..).
opPostExecute:
Runs on the UI thread after doInBackground (Params. ..). The specified result is the value returned by doInBackground (Params. ..).
This method won't be invoked if the task was cancelled.
The order in which the calls
One. onPreExecute
Two. doInbackground
Three. onPostExecute
However doInbackground operate in the other thread.
Must be treated so well doInbackground result is passed three times because of onPreExecute() to create a progress bar progress bar to end its onPostExecute could work.
Please try this
public class LoadData extends AsyncTask<Void, Void, Void> {
ProgressDialog progressDialog;
//declare other objects as per your need
#Override
protected void onPreExecute()
{
progressDialog= ProgressDialog.show(YourActivity.this, "Progress Dialog Title Text","Process Description Text", true);
//do initialization of required objects objects here
};
#Override
protected Void doInBackground(Void... params)
{
//do loading operation here
return null;
}
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
progressDialog.dismiss();
};
}
You can call this using
LoadData task = new LoadData();
task.execute();
To check whether your Service is still running::
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (MyService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
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.
As far as I understand once an AsyncTask is called, the result is changed to null and the AsyncTask is cancelled. Is there a way to retain the result and pass it to onPostExecute(String result). developer.android.com says not to call these functions explicitly.
The app basically scans images and if a user cancels the async task, I'd like the async task to display the images scanned so far. So the result should not be set to null.
Is this possible to accomplish? If yes, how?
class openCVOperation extends AsyncTask<String, String, String>{
private MainActivity context = null;
/*lots of variables here*/
public openCVOperation(MainActivity context1) {
context = context1;// set context from mainActivity
// which
// inherits Activity super class.
// Needed
// for accessing widgets.
}
#Override
protected void onPreExecute() {
pd = new ProgressDialog(context);
pd.setIndeterminate(false);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setMax(100);
pd.setCancelable(true);
pd.setCanceledOnTouchOutside(false);
pd.setMessage("Starting up");
pd.show();
}
#Override
protected String doInBackground(String... params) {
publishProgress("Finding path to Storage...");
path = Environment.getExternalStorageDirectory();
p = path.getAbsolutePath();
p=p+"/location";
rm(p);// this has a loop!
return null;
}
#Override
protected void onCancelled()
{
System.out.println("In onCancelled");
super.onCancelled();
}
#Override
protected void onPostExecute(String result) {
pd.dismiss();
/*post execute stuff*
}
rm(p) has a loop, so I tried using isCancelled() as a condition, but that didn't work.
In the doInBackground
if (isCancelled())
{
return // image so far
}
onPostExecute(String result)
{
// show result
}
I just had to add this to my doInBackground()
pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
// TODO Auto-generated method stub
task.cancel(true);
}
});
Where pd is my progress dialog.
Also make sure you check for isCancelled() in doInBackground() or onCancelled() will never be invoked and the application will force close.
Collect the results:
public class MyTask extends AsyncTask<Void,Void,Void>{
private final List<String> data;
public MyTask(){
data = new ArrayList<String>();
}
public synchronized List<String> getData(){
return new ArrayList<String>(data); //--current data snapshot--
}
private synchronized collect(String s){
data.add(s);
}
#Override
public Void doInBackground(Void...args){
//---do stuff--
collect(/*-stuff-*/);
}
}
You won't lose anything even if thread is interrupted.
If onCancelled is not being called, then your rm method is still running.
Because as you mentioned, it's running a loop.
The best way to control the process (know if it needs to be stopped) is by polling or tediously checking the status of a volatile boolean variable within your rm method.
For example, create a static volatile boolean variable within your AsyncTask class called cancel. Set this variable to false in the onPreExecute method.
In your rm method, check to see if cancel is true before and after the heavy tasks (opening a file, reading a file, part of a download loop).
If it's true, then break out of the method with a return statement.
Better yet, make your rm method return an Integer, 0 for Good and 1 for cancelled.
And finally, right before the doInBackground method hits return, see if you need to call a cancel on the thread or not.
public class asyncTask extends AsyncTask<Void, Void, Void>
{
private static synchronized boolean cancel;
protected void onPreExecute()
{
cancel = false;
}
protected String doInBackground(Void ... params)
{
rm(p);
if(cancel)
asyncTask.cancel;
else
return null;
}
protected void onCancelled()
{
// only executed if doInBackground resulted in a cancel == true
}
protected void onPostExecute(Void param)
{
/// only executed if doInBackground resulted in a cancel == false
}
private int rm(String str)
{
if(cancel)
return 1;
//do part of task
if(cancel)
return 1;
//another part of task
if(cancel)
return 1;
//another part of task
return cancel ? 1 : 0;
}
}
I have a button on my app, if the user click it, it will refresh the current page by calling onResume(), and there are lots of database operations in onResume(). Instead of keeping the button stay pressed for a while, I would like to use asynctask to make a progressdialog while loading the data. But the problem is that the button will still be in pressed state and the progressdialog only show at the end of the operation for a very short duration.
RefreshButton.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
onResume();
}
});
protected void onResume()
{
doneloading = false;
monResumeloading = new onResumeloading();
monResumeloading.execute();
....loading...
doneloading = true;
}
private class onResumeloading extends AsyncTask<Integer, Integer, String>
{
private ProgressDialog progressDialog;
#Override
protected void onPostExecute(String result)
{
progressDialog.dismiss();
}
#Override
protected void onPreExecute()
{
progressDialog = new ProgressDialog(StatisticsActivity.this);
progressDialog.setMessage("Loading...");
progressDialog.setCancelable(true);
progressDialog.show();
Log.i(TAG, " doneloading=false");
}
#Override
protected void onProgressUpdate(Integer... values)
{
}
#Override
protected String doInBackground(Integer... params)
{
while(!doneloading)
{
publishProgress(0); //dummy
log.i(TAG, "loading");
}
return null;
}
}
I observed that the "loading" log is showing right after the asynctask execution and stop right after the boolean doneloading becomes false. But the progressdialog is not working properly. Please help me :(
First thing, I don't think you should be calling your AsyncTask in the onResume() function. You can simply call it from your ClickListener.
Right now, you are doing your '....loading...' code before you even execute your AsyncTask. That's why the button stays pressed while it's executing '....loading...' and then when it's done, it executes your AsyncTask which really isn't doing anything - that's why it just shows up for a short duration.
Move your '....loading...' code into your doInBackground() of your AsyncTask and it should work ok.
Summary:
Click: Execute AsyncTask
AsyncTask: opens ProgressDialog
AsyncTask: Executes your '...loading...' code
AsyncTask: Wait for '...loading...' code to complete while still displaying dialog.
AsyncTask: Dismiss ProgressDialog