0

an Android 4+ app should perform a long running operation. This could be copying a million files from A to B for example. To not block the UI this operation runs in the background using an AsyncTask.

Assume that the operation needs some user feedback in the middle of the process to continue its work, e.g. "File XY already exists. Override, Irgnore or Rename?"

What is the best way to get this feedback from the user? Since the operation is running in a background thread one could not just present an AlertDialog (or something similar) since UI interaction is only possible in the main thread...

So for I came across these solution:

  1. Ask for feeback before background threads starts, e.g. ask how to handle conflicts before starting to copy/move the files in the background.
  2. Do not handle conflicts but note them to ask the user how to handle them after the operation is complete in a new operation.
  3. End the background operation on the first conflict, ask the user for feedback and continue a new background operation

I do not like any of these solutions. In the first case the user is asked for feedback even if there will be no conflict at all. The second solutions is not possible if the steps have to be processed in a specific order. The third solution would result in code that is very difficult to read/understand/maintain.

A good solution would be:

  • Stop the background thread
  • Marshal to the UI thread and get feedback from the user
  • Resume background thread and use feedback to continue the operation

Using GCD in Objectiv-C/iOS or async/await in C# this is not a big problem. But how can this be done in Android using AsyncTask?

Andrei Herford
  • 17,570
  • 19
  • 91
  • 225

1 Answers1

0

Meanwhile I thought, that I found an answer here: Simply run myActivity.runOnUiThread(...) within doInBackground(...) and wait for it. Sounds good, but it does not work. The AsyncTask/background thread does NOT wait for the Runnable to finish:

private void copyFiles() {
    CopyTask copyTask = new CopyTask (this);
    copyTask.execute();
}

private class CopyTask extends CustomAsyncTask<Void, Void, Void> {
    private doCopy;

    @Override
    protected Boolean doInBackground(Void... params) {              
        // Custom code, e.g. copy files from A to B and check for conflict
        for (File file : allFiles) {
            doCopy = true;

            if (isConflict(file)) {
                // Stop current thread and ask for user feedback on UI Thread

                Runnable uiRunnable = new Runnable() {
                    public void run() {
                        // Pos 1. --> Execute custom code, e.g. use AlertDialog to ask user if file should be replaced...
                        doCopy = false;

                        synchronized (this) {
                           this.notify();
                        }
                    }
                });  

                synchronized(uiRunnable) {
                    // Execute code on UI thread
                    activity.runOnUiThread(uiRunnable);

                    // Wait until runnable finished
                    try {
                        uiRunnable.wait();
                    }
                    catch (InterruptedException e ) {
                        e.printStackTrace();
                    }
                }
            }

            // Pos 2. --> Continue work
            if (doCopy)
               copyFromAToB(File);
        }

        return null;
    }
}

I would expect, that when a conflict is detected the Runnable is executed and and Pos 1 (code inside Runnable to resolve conflict) is executed BEVOR Pos 2 is reached. This is not the case. The Runnable is executed correctly but the AsyncTask does not wait for it to finish. The execution of doInBackground is continued without any interruption. It seems that doInBackground and the Runnable are executed in parallel (not suprising since they are executed on different threads) but why does doInBackground not wait?

Community
  • 1
  • 1
Andrei Herford
  • 17,570
  • 19
  • 91
  • 225