Question

I have a class that sends various mail with attachments.

Because the method sendEmail(addresses); requires time to upload the attachments and send the emails, I have created an AsyncTask to show a waiting dialog,

however, unfortunately, during the sending the app freezes until it has terminated the sending without showing anything (or sometimes shows a frozen waiting windows for a little instant before the end of procedure)

The related parts of code in my class are the follows

public class MyClass extends Activity {
    ...

    private ProgressDialog waitingDialog;

    ....

    OnClickListener mInvia = new OnClickListener() {
        public void onClick(View v) {
                new MyAsyncTaskClass().execute(new String[] {});
        }
    };

    public void prepareSending() {
        String x = "";
        for (String s : selectedMails) {
            x += (s + ";");
        }
        String[] addresses = x.split(";");
        sendEmail(addresses);
    }

    private void openFile() {
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.setType("file/*");
        startActivityForResult(i, FILE_REQ_CODE);
    }

    protected void onActivityResult(int requestCode, int resultCode,
            Intent intentData) {

        Uri tmp = intentData.getData();
        filePath=getRealPath(tmp);
        super.onActivityResult(requestCode, resultCode, intentData);

    }


    public void sendEmail(String[] addresses) {

        Mail m = new Mail("sendermail@sample.com",
                "senderpassword");

        name = editor1.getText().toString();
        subject = editor2.getText().toString();
        text = editor3.getText().toString();
        emailReply = editor4.getText().toString();

        m.setTo(addresses);
        m.setFrom(emailReply);
        m.setSubject(subject);
        m.setBody(text + "\n\n\n Sent by" + name);
        try {
            m.send();
        } catch (Exception e) {
            Log.e("MyClass", "Cannot send email", e);
        }

        try {
            m.addAttachment(filePath);

            if (m.send()) {
                Alerts.Ok(MyClass.this);
                nSuccess++;
            } else {
                Alerts.ErrorSending(MyClass.this);
            }
        } catch (Exception e) {
            Alerts.ErrorAttachment(MyClass.this);
        }

    }

    //inner class that should show a waiting windows
    private class MyAsyncTaskClass extends AsyncTask<String, Void, Void> {

        @Override
        protected void onPreExecute() {
            waitingDialog = new ProgressDialog(MyClass.this);
            waitingDialog.setMessage("Loading ....");
            waitingDialog.setIndeterminate(true);
            waitingDialog.setCancelable(true);
            waitingDialog.show();
        }

        @Override
        protected Void doInBackground(final String... strings) {
            try {
                runOnUiThread(new Runnable() {
                    public void run() {
                        prepareSending();
                    }

                });
            } catch (Exception e) {

            }
            return null;
        }

        @Override
        protected void onPostExecute(Void params) {
            waitingDialog.dismiss();
        }
    }

    //end innerclass
    // start context menu code
    ......

}
Was it helpful?

Solution

Problem

I'd wager it's because you're running everything on the UI thread:

    @Override
    protected Void doInBackground(final String... strings) {
        try {
            runOnUiThread(new Runnable() {
                public void run() {
                    prepareSending();
                }

            });
        } catch (Exception e) {

        }
        return null;
    }

Do this instead!

Run all your doInBackground code based on non-UI-reliant things.

    @Override
    protected Void doInBackground(final String... strings) {
        prepareSending();
        return null;
    }

Accessing the UI

If you need to access data from the UI, or anything else on the UI thread, do that first.

    protected void onPreExecute() {
        // Find all your "editor" fields here, and save them to member variables within the AsyncTask.
        // ...
    }

Then send those to your method

In prepareSending and sendEmail accept that data as parameters:

public void prepareSending(String name, String subject, String text, String emailReply) {
    // ...
    sendEmail(addresses, name, subject, text, emailReply);
}
public void sendEmail(String[] addresses, String name, String subject, String text, String emailReply) {
    // ...
}

And lastly, you need to send those from doInBackground:

    @Override
    protected Void doInBackground(final String... strings) {
        prepareSending(mName, mSubject, mText, mEmailReply); // The local fields you saved earlier
        return null;
    }

OTHER TIPS

This is not the recommended way of doing:

    @Override
    protected Void doInBackground(final String... strings) {
        try {
            runOnUiThread(new Runnable() {
                public void run() {
                    prepareSending();
                }

            });
        } catch (Exception e) {

        }
        return null;
    }

You should use publishProgress() in order to run stuff on the UI thread as your processing is going on.

That said, you are also not managing the cases where the activity can be destroyed. This could lead to memory leaks and multiple AsyncTasks running in the background.

Try to get to something like:

public static class MyAsyncTask extends AsyncTask<Void, Void, Void> {

  MyAsyncTask(MyActivity a) {
    activity = new WeakReference<MyActivity>(a);
  }

  @Override
  protected void onPreExecute() {
    if (activity.get()!=null) activity.get().showDialog();
  }

  @Override
  protected Void doInBackground(Void... params) {
    // Upload data from here
  }
  @Override
  protected void onPostExecute(Void params) {    
    if (activity.get()!=null) activity.get().dismissDialog();
  }


  private WeakReference<MyActivity> activity;
}

Your activity is respondible for saving the task as a non-instance state, closing and recreating the dialog when appropriate, ...

Finally, pass all the information needed to send the mail to your task constructor (content, subject, ...). All the UI accesses will then be done on the UI thread when you create the task (not in doInBackground as you do curently).

Problems lies here:

try {
        runOnUiThread(new Runnable() {
                public void run() {
                    prepareSending();
                }

            });
    } catch (Exception e) {

    }

1) prepareSending(); method is called on the UI thread. Refer this to understand how to use AsyncTask in Android

2) Also it seems like you have habit of catching generic exceptions and also not handling them. Refer Code Style Guidelines. It will help you a lot.

Good Luck ;)

private class MyAsyncTaskClass extends AsyncTask<String, Integer, Integer> {
        ProgressDialog waitingDialog;

        @Override
        protected void onPreExecute() {
            waitingDialog = new ProgressDialog(MyClass.this);
            waitingDialog.setMessage("Loading ....");
            waitingDialog.setIndeterminate(true);
            waitingDialog.setCancelable(true);
        }

        @Override
        protected Integer doInBackground(final String... strings) {
            setProgress(-1);
            // long process start
            prepareSending();
            // long process end
            setProgress(-2);
            return 0;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {

            if (values[0] == -1) {
                waitingDialog.show();
            } else if (values[2] == -2) {
                waitingDialog.dismiss();
            }
        }

        @Override
        protected Integer onPostExecute(Integer params) {
        }
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top