I am getting weird behavior when executing AsyncTask from AlertDialog. I need some suggestion/workaround to fix it. I am stuck at this point.
When I execute AsyncTask from AlertDialog, it is not calling onPostExecute. It call doInBackground, but after finish, it doesn't call onPostExecute. I want AsyncTask to get executed on the basis of AlertDialog's button press value.
Here is the function that creates AlertDialog and executes AsyncTask:
private void processDownloadChoosen(String msg, int __position){
final AlertDialog.Builder alertBox = new AlertDialog.Builder(new ContextThemeWrapper(ShivaniMP3Activity.this, android.R.style.Theme_Dialog));
final int position = __position;
alertBox.setMessage(msg);
alertBox.setCancelable(false)
.setPositiveButton("Download", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog,int id){
dialog.dismiss();
String downloadURL = entries.get(position);
AsyncTaskDownload atd = new AsyncTaskDownload(downloadURL);
if((downloadURL != null) &&(downloadURL != "")){
EnglishMP3Activity.totalDownloads++;
if(downloadWindow == null){
downloadWindow = new PopupWindow(downloadPopupLayout, LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT, true);
downloadWindow.showAtLocation(downloadPopupLayout, Gravity.CENTER, 0, 0);
}
atd.execute();
}
}
}).setNegativeButton("Listen", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog,int id){
dialog.dismiss();
String downloadURL = entries.get(position).replace("%20", " ");
emp = new EasyMediaPlayer(mp3PopupLayout,buttonPlayPause,seekBarProgress,tv_mp3,downloadURL);
emp.startPlayingMP3();
}
}).show();
}
And I am calling this function from listview's on item click:
//lv is listview
lv.setOnItemClickListener(new AdapterView.OnItemClickListener(){
public void onItemClick(AdapterView<?> p, View v, int position, long id) {
processDownloadChoosen("Do you want to Listen or Download this file ?",position);
}
});
My AsyncTask definition look like:
public class AsyncTaskDownload extends AsyncTask<Void, String, Void> {
//
protected void onPreExecute(){
pBar1.setVisibility(View.INVISIBLE);
//
}
protected Void doInBackground(Void... vd){
try{
//do something
}
catch(Exception e){
//do somting
}
return null;
}
protected void onProgressUpdate(String... msg) {
//do smething
}
protected void onPostExecute(Void in){
cancelDownloadButton.setVisibility(View.GONE);
//do smtthing
}
}
PLEASE NOTE: When I execute AsyncTask directly from ListView's on item click function, all works well. But while calling from AlertDialog, it doesn't call onPostExecute.
Any help to resolve/workaround for this is appreciated.. Advance Thanks
Try using a handler/Runnable such that in the alertDialog's onPositiveClick callback you do
Handler mHandler;
prtected void onCreate(Bundle sis) {
//other code
this.mHandler = new Handler();
}
//In your DialogInterface.OnClickListener
mHandler.post(new Runnable() {
#Override
public void run() {
//Init and launch your asynctask here
}
});
The reason this is not working is that an AsyncTask must be invoked on the UI thread and im pretty sure that invoking on the positive button callback is not linked directly with the activities context (given that the task works from the ListView's onItemClick callback)
After going through several posts, it looks like AsyncTask doesn't work properly from AlertDialog.
So I achieved this by "fooling" Android. Noe I am doing everything inside doInBackground function and also getting the feel as if preexecute/postexecute is getting called.
Below is what i did:
1) Removed onPreExecute() and onProgressUpdate() from AsyncTask.
2) define doInBackground something like:
protected Void doInBackground(Void... vd){
publishProgress("I_AM_STARTED");
//Other Work
publishProgress("I_AM_DONE");
}
3) define onProgressUpdate something like:
protected void onProgressUpdate(String... msg) {
if(msg[0].equals("I_AM_STARTED")){
//code for onPreExecute
}
else if(msg[0].equals("I_AM_DONE")){
//code for onPostExecute
}
else{
//normal code
}
}
Hope this may be helpful for someone who got stuck like me. :-)
Related
I have an AsyncTask that does a bunch of stuff in the doInBackground() portion, and in between that bunch of stuff, I need to wait for the user to physically do something before I can continue. How do I popup some dialog for the user to click OK before continuing?
Thanks!
in between that bunch of stuff, I need to wait for the user to
physically do something before I can continue.
You aren't supposed to do that in doInBackground method, You need to do that in onPostExecute(); Interaction with the user should be done in onPostExecute.
What you can do here?
Divide your code in 2 parts, perform the code that has to be done until users Interaction in the background in doInBackground, make user Interact do that in onPostExecute, after that for rest of remaining code you can use another AsyncTask.
class LoadData extends AsyncTask<Object, Object, Object>
{
#Override
protected Object doInBackground(Object... p_params)
{
// Your background code
return null;
}
#Override
protected void onPreExecute()
{
// Display Progress dialog with cancelable false
super.onPreExecute();
}
#Override
protected void onPostExecute(Object p_result)
{
// Dismiss Progress dialog
super.onPostExecute(p_result);
}
}
If you want to put wait dialog in between doInBackground section then you can try following code:
#Override
protected Void doInBackground(Void... params) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
final Dialog dialog = new Dialog(activity);
dialog.setTitle("Demo");
Button button = new Button(activity);
button.setText("Press For Process..");
dialog.setContentView(button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(activity, "Perform Task",
Toast.LENGTH_LONG).show();
// You can perform task whatever want to do after
// on user press the button
dialog.dismiss();
}
});
dialog.show();
}
});
return null;
}
I display an Alertbox with ok or cancel.
I want to implement an asynch task on the press of OK. Havent done asynch and been struggling with it for awhile. I dont understand where the asych class goes also. Does it go outside the method that is being executed or outside of it? Current code as follows:
private abstract class DoAsynchTask extends AsyncTask<Void,Void,Void>
{
protected void doInBackground()
{
Drawable drawable= getImage(imageSelect);
MakeWallPaper(drawable,1);
}
/* protected void onProgressUpdate(Integer... progress)
{
setProgress(progress[0]);
}*/
protected void onPostExecute()
{
Toast.makeText(getApplicationContext(), "Wallpaper Saved.",Toast.LENGTH_LONG).show();
AlertDialogProcessing=0;
}
}
public void getWallpaper(final View v)
{
if(AlertDialogProcessing==0)
{
final String title="Set Image to Wallpaper";
final String message="Press OK to set as Wallpaper or CANCEL.\nWait after pushing OK.";
final String ok="OK";
final String cancel="CANCEL";
final AlertDialog.Builder alertbox = new AlertDialog.Builder(this);
alertbox.setCancelable(true);
alertbox.setIcon(android.R.drawable.ic_dialog_alert);
alertbox.setTitle(title);
alertbox.setMessage(message);
alertbox.setNegativeButton(cancel, null);
final AlertDialog dlg = alertbox.create();
alertbox.setPositiveButton(ok,new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dlg, int which)
{
DoAsynchTask.execute(null,null,null); //<<<<Wrong
dlg.dismiss();
Vibrate(ClickVibrate);
}
});
alertbox.setNegativeButton(cancel,new DialogInterface.OnClickListener(){ public void onClick(DialogInterface arg0, int arg1){AlertDialogProcessing=0;
Vibrate(ClickVibrate); } });
alertbox.show();
}
}
There's a couple problems in the code.
1) First of all, the compiler is probably giving you this message:
The type MyActivity.DoAsynchTask must implement the inherited abstract
method
AsyncTask.doInBackground(Void...) MyActivity.java
If you look closely at the error message, you'll realize that what you defined was this:
protected void doInBackground() {
which is not what is needed. Even though it might seem silly, when your AsyncTask subclass takes Void as the generic parameter types, that means that doInBackground() must look like this:
protected Void doInBackground(Void... arg0) {
The compiler complains because you haven't implemented that (exact) method. When you inherit from an abstract class, and fail to implement all of its required/abstract method(s), then you can only get it to compile by marking the subclass as abstract, too. But, that's not really what you want.
So, just change your code to (remove abstract from your class):
private class DoAsynchTask extends AsyncTask<Void,Void,Void>
and
protected Void doInBackground(Void... arg0) {
{
Drawable drawable= getImage(imageSelect);
MakeWallPaper(drawable,1);
return null;
}
2) And the second problem, as others have pointed out, is that you must start your task with:
new DoAsynchTask().execute();
not
DoAsynchTask.execute(null,null,null);
Your code would only be correct if execute() was a static method in AsyncTask, which it's not. In order to invoke the non-static execute() method, you first need a new instance of the DoAsynchTask class. Finally, the null, null, null parameter list is also not necessary, although I don't think it will cause the code to fail either.
Since your doInBackground() does not specify any parameters, you should call DoAsynchTask.execute() without parameters.
Why is your class abstract? Normally an AsyncTask should be an inner class of the activity starting it. So create your dialog in the activity, and execute the AsyncTask when clicking on OK button, like you do.
//final working copy -Thanks ALL
public void getWallpaper(final View v)
{
Vibrate(ClickVibrate);
final class SetWallPaperAsynchTask extends AsyncTask<Void,Void,Void>
{
#Override
protected Void doInBackground(Void... arg0)
{
Drawable drawable= getImage(imageSelect);
MakeWallPaper(drawable,1);
return null;
}
#Override
protected void onPostExecute(Void result)
{Toast.makeText(getBaseContext(), "Wallpaper Saved.", Toast.LENGTH_LONG).show();
AlertDialogProcessing=0;
}
}
if(AlertDialogProcessing==0)
{
ProgressDialog progress;
final String title="Set Image to Wallpaper";
final String message="Press OK to set as Wallpaper or CANCEL.";
final String ok="OK";
final String cancel="CANCEL";
final AlertDialog.Builder alertbox = new AlertDialog.Builder(this);
alertbox.setCancelable(true);
alertbox.setIcon(android.R.drawable.ic_dialog_alert);
alertbox.setTitle(title);
alertbox.setMessage(message);
alertbox.setNegativeButton(cancel, null);
final AlertDialog dlg = alertbox.create();
alertbox.setPositiveButton(ok,new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface arg0, int arg1)
{
new SetWallPaperAsynchTask().execute();
dlg.dismiss();
Vibrate(ClickVibrate);
}
});
alertbox.setNegativeButton(cancel,new DialogInterface.OnClickListener(){ public void onClick(DialogInterface arg0, int arg1){AlertDialogProcessing=0; Vibrate(ClickVibrate); } });
alertbox.show();
}
}
This question already has answers here:
Can't create handler inside thread that has not called Looper.prepare()
(30 answers)
Closed 8 years ago.
This is my code. When I click on my app logo, after the splash screen, this is the class that is first called from an intent. But, after the tab is loaded, and onPreExecute() is once executed, the app crashes.
public class HomeActivity extends Activity{
private static final String dialog = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user_main_tab_home);
new HomeDownloadPage().execute();
}
public class HomeDownloadPage extends AsyncTask<String,Void,String>{
private final ProgressDialog dialog = new ProgressDialog(HomeActivity.this);
protected void onPreExecute() {
this.dialog.setMessage("Have Paitence! ");
this.dialog.show();
}
#Override
protected String doInBackground(String... params) {
User user = null;
try {
user = new User("4eeb");
user.getList();
/*
* Custom adapter
* */
ArrayList<User> users = new ArrayList<User>();
for(User u : user.following){
users.add(u);
}
ListView lv = (ListView) findViewById(R.id.user_list);
final UserFollowingListAdapter csl = new UserFollowingListAdapter(HomeActivity.this,R.layout.user_list,users,this);
OnItemClickListener listener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
Object o = csl.getItem(position);
setTitle(parent.getItemAtPosition(position).toString());
}
};
lv.setAdapter(csl);
lv.setOnItemClickListener(listener);
/*
* Onclick listener
* */
lv.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
Intent i = new Intent("com.list.SEARCH");
Toast.makeText(HomeActivity.this, "rowitem clicked", Toast.LENGTH_LONG).show();
// TODO Auto-generated method stub
}
});
} catch (Exception e) {
showError();
}
return null;
}
protected void onPostExecute(String result) {
// execution of result of Long time consuming operation
}
}
public void showError(){
new AlertDialog.Builder(HomeActivity.this)
.setTitle(" Oops , Server down :( ")
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
}
//
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
}
}
Error I get is at the doInBackground() function.
Exact error: 01-19 19:03:01.264: E/AndroidRuntime(1138): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
What is the problem?
You are attempting to do things involving the UI (ListView lv = (ListView) findViewById(R.id.user_list);) within a background thread. You can not do this. You may process information in the background, then pass it back to the UI thread and update the UI
As pyrodante said, you're attempting to modify the UI while not on the UI thread. If you want modify the UI from a non-UI thread, you can use the runOnUiThread() function. That said, there's a better solution to your problem. You really should be using a Loader. They were basically designed to address exactly what you're trying to do. Note that even if you're designing an app that's pre-3.0, you can still access loaders via the Android Support package.
For example I have following AsyncTask:
private class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {
#Override
protected Void doInBackground(Void... params) {
try {
//some code that may throws exception
return true;
} catch (IOException ex) {
return false;
}
}
#Override
protected void onPostExecute(Boolean param){
if (!param) {
AlertDialog.Builder builder = new AlertDialog.Builder(MyActivity.this);
builder.setMessage("Error");
builder.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//I want to retry MyAsyncTask here
}
});
builder.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
AlertDialog alert = builder.create();
alert.show();
}
}
}
What is best practice to do this? I'm afraid of recursion this code.
You cannot strictly "retry" an instance of AsyncTask. You have to create a new instance and execute it, just as you did the first one. You should not encounter a recursion problem.
I retry the code within doInBackground() for x number of times:
#Override
protected Void doInBackground(Void... params)
{
final int MAX_RETRY=3;
int iLoop;
boolean bSuccess=true;
for (iLoop=0; iLoop<MAX_RETRY; iLoop++)
{
try
{
//do some code that throw an exception
//success! exit loop
iLoop=0;
break;
}
catch (IOException ex)
{
bSuccess=false;
}
}
if (iLoop==(MAX_RETRY-1))
{
bSuccess=false;
}
return bSuccess;
}
This is also one of those times when the two values of a boolean are inadequate to measure success. You could replace bSuccess with an enum for a third measure of success: retry later.
I solved the same problem using BetterASyncTask.
It provides the handy HandleError abstract method, which allows me to catch the exception on the UI thread and decide if retry and how.
Please take a look at retryableasynctask and see if it helps.
Usage:
// Params, Progress and Result could be anything, same as a regular AsyncTask
new RetryableAsyncTask<Params, Progress, Result>(myActivity) {
#Override
protected void onPreExecute() {
// write some code here
}
#Override
protected Result doInBackground(Params... params) {
// execute some expensive task here with your params
// eg: MyExpensiveTask with method called 'get'
return MyExpensiveTask.get(params);
}
#Override
protected void onPostExecute(Result result) {
// write some code here with your result
}
}.execute(myParams);
Overriding "onError" behaviour
By the default, onError method shows a dialog "Cancel" and "Retry" button options. However, you might wanna do something else when something goes wrong. To do so, override onError with your own error handling.
// Params, Progress and Result could be anything, same as a regular AsyncTask
new RetryableAsyncTask<Params, Progress, Result>(myActivity) {
// ...
#Override
protected void onError(Throwable error, final Params... params) {
// write your own error handling
}
}.execute(myParams);
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();
}
}