3

Edit: this is NOT the same as ProgressDialog doesn't show just flashes

I make sure to used AsyncTasks for network communications and file operations, showing an indeterminate, cancelable ProgressDialog until the task is complete, as I feel I properly should.

In some situations--slow mobile Internet connection, an older underpowered device--this may take some seconds. But the exact same task may complete very rapidly in other situations or on other devices, causing the ProgressDialog to show only for a fraction of an instant, basically flickering on the screen.

What is best practice here? I can think of several options:

  1. Leave it as is, knowing that some users will have that flickering effect.
  2. Force the ProgressDialog to stay visible for a minimum length of time (half a second, a second) before it disappears, even if the task has already completed--but I hate the idea of introducing artificial delays.
  3. Time the task and only show the ProgressDialog if the task takes more than a minimum set amount of time, such as half a second. The problem is this is not really a solution; if the task takes under that length of time, great; if the task takes a long time, great--but if the minimum time of 0.5 seconds and the task actually takes 0.7 seconds, then you have the same issue again... a brief flicker.
  4. If you can monitor the progress of the task, test it briefly and estimate, from the first fraction of a second or the first x percent, how long it should take to complete the task; if it is expected to take a while, then show the ProgressDialog. But this would be quite complex, and not every background task lends itself well to this sort of progress estimation.

What is the best approach? Is there an option I've missed? Is there a way to set the ProgressDialog so it only shows if there will be a noticeable delay? (I doubt there is, but it would be nice if there was some magic way). Is one of these the "standard" solution?

Community
  • 1
  • 1
Chad Schultz
  • 7,770
  • 6
  • 57
  • 96
  • 3
    there is no standard approach, you've got all the right ideas. Showing your ProgressDialog, then running your AsyncTask after a small delay (e.g. 100ms) isn't a terrible approach. You could also just do a ProgressBar (i.e. spinner), a smaller more subtle UI element than the Dialog. – CSmith Mar 02 '12 at 22:00
  • I agree with CSmith, I would consider taking it out of the dialong and just adding it to the UI somewhere. That way it is not jarring, but it is still there when needed. – FoamyGuy Mar 02 '12 at 22:08

3 Answers3

2

Force the ProgressDialog to stay visible for a minimum length of time (half a second, a second) before it disappears, even if the task has already completed--but I hate the idea of introducing artificial delays.

This is a better solution than letting the progress dialog just flash briefly on the screen. From a user perspective it introduces consistency. If it shows up some times and not others it seems like something isnt working correctly.

I added a long variable to my async task and, on pre-execute, set it to System.nanoTime(). Then in post-execute I calulate how much time passed and compare it to a set minimum time. If its under that amount of time, I use wait() for the remaining time. Currently I have it set to a 1.5 sec delay to make sure there is enough time to read the progress dialog before it vanishes.

private final class SyncPendingOrders extends AsyncTask<String, Integer, String> {

    long timer = 0;

    @Override
    protected void onPreExecute(){
        timer = System.nanoTime();

        pd = ProgressDialog.show(context,null,"Updating Pending Orders",true,false);
        pd.setIndeterminateDrawable(context.getResources().getDrawable(R.anim.spinner));
    }

    @Override
    protected String doInBackground(String... args) {

        updatePendingOrders();

        return null;
    }

    @Override
    protected void onPostExecute(String me){

        long clock = System.nanoTime()-timer - 2000000000;

        if(clock > 0)
        {
            try{
                wait(clock);
            }catch(Throwable e){

            }
        }

        PendingOrdersView();
        pd.dismiss();
    }

}

This way the user always sees the same box and knows what the program is doing. Consistency is key.

Norman
  • 21
  • 3
0

Keep the progress dialog as is, especially for network operations, since you might be getting lucky in a development environment, but also because you won't be able to control the speed at which your internet connections work in a real world scenario.

Mike D
  • 4,938
  • 6
  • 43
  • 99
0

Thanks to CSmith's suggestion, I tried adding this to my AsyncTask doing all the background work:

@Override
        protected void onCancelled() {
            completed = true;
            super.onCancelled();
            dismissProgressDialog(); //calls handler.post and dismisses dialog
        }

        @Override
        protected void onPostExecute(Void result) {
            completed = true;
            super.onPostExecute(result);
            dismissProgressDialog();
        }
@Override
        protected void onPreExecute() {
            super.onPreExecute();


            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ie) {
                        return;
                    }
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (!completed) {
                                progressDialog = ProgressDialog.show(activity, activity.getString(R.string.loading_title), null, true, true, new DialogInterface.OnCancelListener() {
                                    @Override
                                    public void onCancel(DialogInterface dialog) {
                                        cancel(true);
                                    }
                                });
                            }
                        }
                    });

                }
            }).start();
        }

If the task completes in less time than the indicated delay (500 milliseconds, in this case), the dialog is not shown. If it takes longer, then it is shown after the delay. Note that this does not completely prevent "flickering"... if, for example, the task took one second then the dialog would flicker for half a second.

This seems to help, but I'm not completely satisfied with the solution and welcome suggestions.

Chad Schultz
  • 7,770
  • 6
  • 57
  • 96