Android AsyncTask implementation principle and tips sharing

Why use AsyncTask

We all write apps on the principle that the main thread cannot run tasks that require a lot of CPU time slices, such as a large number of complex floating-point operations, large disk IO operations, network socket s, etc., which will lead to slow response of our main thread to users and even ANR, which will deteriorate the user experience of the application, However, sometimes these time-consuming tasks do need to be performed, so we can usually use AsyncTask or new Thread
In this way, putting the task into the working thread for execution will not occupy the time slice of the main thread, so the main thread will respond to the user's operation in time. If we use new Thread to execute the task, we need to maintain a lot of additional code if we need to cancel the task execution or return the task execution result, AsyncTask is implemented based on the concurrent classes provided by the concurrent shelf package. The above two requirements have been encapsulated for us, which is why we chose AsyncTask.

How to use AsyncTask

Let's briefly introduce some usage examples of AsyncTask. Let's create a new class DemoAsyncTask to inherit AsyncTask, because AsyncTask is an abstract class, and the doInBackground method must be overridden.

private class DemoAsyncTask extends AsyncTask<String, Void, Void> {
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
@Override
protected Void doInBackground(String... params) {
    return null;
}   

@Override
protected void onPostExecute(Void aVoid) {
    super.onPostExecute(aVoid);
}

@Override
protected void onProgressUpdate(Void... values) {
    super.onProgressUpdate(values);
}

@Override
protected void onCancelled(Void aVoid) {
    super.onCancelled(aVoid);
}

@Override
protected void onCancelled() {
    super.onCancelled();
}

}

DemoAsyncTask task = new DemoAsyncTask();
task.execute("demo test AsyncTask");
//task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, "test");
//myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "test");

Simple analysis
The above is the simplest way to use AsyncTask. Among the methods we rewritten above, onInBackground runs on the working thread and all other methods run on the main thread. In addition, Android provides us with two methods, which are listed above.

1. The first method will use the default Executor to execute our tasks, which is actually SERIAL_EXECUTOR,SERIAL_ In fact, we can customize Executor through methods. The default implementation of Android helps us execute tasks one by one, that is, single thread. There is still a very interesting history about whether AsyncTask's task execution is single thread implementation or multi-threaded implementation. The earlier version was single thread implementation. Starting from Android 2. X, Google changed it to multi-threaded implementation, Later, Google found that if multithreading is implemented, there will be a lot of extra work to ensure thread safety for developers. Therefore, starting from Android 3.0, the default implementation is changed to single thread. Today we demonstrate that the code version of framework is 21 (Android 5.0).

2. It also provides an interface for multithreading implementation, that is, the last line of code asynctask.thread written above_ POOL_ EXECUTOR.

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
  return executeOnExecutor(sDefaultExecutor, params);
}
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

In fact, I believe that you often use AsyncTask in your work and study. Let's go directly to the source code of AsyncTask (insert a digression, you can also summarize your experience in your work and study and write it down ~ ~)

public abstract class AsyncTask<Params, Progress, Result> {
....
}

The AsyncTask abstract class has three generic parameter types. The first is the parameter type you need to pass in, the second is the type of task completion progress, which is generally Integer, and the third is the return type of task completion results. Sometimes these parameters are not all required, and they do not need to be set to Void. In addition, there is only one Result, but there can be multiple Params.
We can see that the default constructor of AsyncTask initializes two objects, mWorker and mFuture.

private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            return postResult(doInBackground(mParams));
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occured while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };

mWoker implements the Callback interface, which is an interface in the advanced concurrency rack package added to JDK1.5. It can have a generic return value.

public interface Callable<V> {
/**
 * Computes a result, or throws an exception if unable to do so.
 *
 * @return computed result
 * @throws Exception if unable to compute a result
 */
V call() throws Exception;
}

FutureTask implements the RunnableFuture interface. The RunnableFuture interface is provided by JDK. You can see from the name that it inherits the Runnable and Future interfaces. mFuture is an instance of FutureTask and can be directly executed by the Executor class instance. Let's continue to look at the execute method of AsyncTask.

public final AsyncTask<Params, Progress, Result>     execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result>    executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

First call the onPreExecute() method, which is still the main thread (strictly calling the AsyncTask execution thread), then exec.execute(mFuture), and assign the task to exec processing. execute mFuture is actually invoke mWoker, then invokes postResult (doInBackground), which is already running in the worker thread pool, and will not block the main thread. Then send message to mHandler_ POST_ RESULT message, then call the finish method, if isCancelled, callback onCancelled, otherwise callback onPostExecute.

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

Now, in fact, we have completed the whole task execution process of AsyncTask, and the callback methods exposed to us have also been completed. Now let's look back. AsyncTask is actually just a package of the advanced concurrency feature provided by JDK 1.5 and the concurrent rack package, which is convenient for developers to handle asynchronous tasks. Of course, there are many detail processing methods worth learning, such as feedback on task execution progress, guarantee of task execution atomicity, etc. these are left for you to learn.

A few tips for using AsyncTask
We illustrate with an example, "click the button to start downloading QQAndroid installation package, and then display a dialog box to feed back the download progress". Let's initialize a dialog box first. To display the progress, we use a progress bar NumberProgressbar on Github that can display the percentage, and the button to start the task. We use * circlebutton *, a button with cool animation. There are many very good open source projects on Github. Of course, cool controls are part of it. There will be a chance later, I will learn some popular controls and their implementation principles. Let's take them for the moment today ~ ~.

1. Initialize the progress bar prompt dialog box first.

builder = new AlertDialog.Builder(
MainActivity.this);
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
mDialogView = inflater.inflate(R.layout.progress_dialog_layout, null);
mNumberProgressBar = (NumberProgressBar)mDialogView.findViewById(R.id.number_progress_bar);
builder.setView(mDialogView);
mDialog = builder.create();

  • 2. Set button click event.
  findViewById(R.id.circle_btn).setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v) {
    dismissDialog();
    mNumberProgressBar.setProgress(0);
    myTask = new MyAsyncTask();
    myTask.execute(qqDownloadUrl);
    }
    });

3. The downloadasynctask implementation is a little long.
private class DownloadAsyncTask extends AsyncTask<String , Integer, String> {

  @Override
  protected void onPreExecute() {
      super.onPreExecute();
      mDialog.show();

  }

  @Override
  protected void onPostExecute(String aVoid) {
      super.onPostExecute(aVoid);
      dismissDialog();
  }

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

      mNumberProgressBar.setProgress(values[0]);
  }

  @Override
  protected void onCancelled(String aVoid) {
      super.onCancelled(aVoid);
      dismissDialog();
  }

  @Override
  protected void onCancelled() {
      super.onCancelled();
      dismissDialog();
  }

  @Override
  protected String doInBackground(String... params) {
      String urlStr = params[0];
      FileOutputStream output = null;
      try {
          URL url = new URL(urlStr);
          HttpURLConnection connection = (HttpURLConnection)url.openConnection();
          String qqApkFile = "qqApkFile";
          File file = new File(Environment.getExternalStorageDirectory() + "/" + qqApkFile);
          if (file.exists()) {
              file.delete();
          }
          file.createNewFile();
          InputStream input = connection.getInputStream();
          output = new FileOutputStream(file);
          int total = connection.getContentLength();
          if (total <= 0) {
              return null;
          }
          int plus = 0;
          int totalRead = 0;
          byte[] buffer = new byte[4*1024];
          while((plus = input.read(buffer)) != -1){
              output.write(buffer);
              totalRead += plus;
              publishProgress(totalRead * 100 / total);
              if (isCancelled()) {
                  break;
              }
          }
          output.flush();
      } catch (MalformedURLException e) {
          e.printStackTrace();
          if (output != null) {
              try {
                  output.close();
              } catch (IOException e2) {
                  e2.printStackTrace();
              }
          }
      } catch (IOException e) {
          e.printStackTrace();
          if (output != null) {
              try {
                  output.close();
              } catch (IOException e2) {
                  e2.printStackTrace();
              }
          }
      } finally {
          if (output != null) {
              try {
                  output.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      return null;
  }

```

}
Such a simple download file is basically realized. So far, there is no skill, but now we have a problem, that is, if our Activity Executing a task in the background may take a long time, and the user may click return to exit Activity Or quit App,The background task will not exit immediately if AsyncTask Internal Activity The reference of member variables in will also cause Activity The recovery delay causes memory leakage for a period of time, so we need to add the following fourth step.
  • 4. In onpause, judge whether the application wants to exit, so as to decide whether to cancel the execution of AsyncTask.
@Override
protected void onPause() {
super.onPause();
if (myTask != null && isFinishing()) {
myTask.cancel(false);
}
}

In this way, our asynchronous tasks will be cancelled when the Activity exits, and will be successfully destroyed and recycled by the system. The fourth step will often be missed, and generally there will be no fatal problems. However, once there is a problem, it is difficult to troubleshoot, so it is necessary to follow the coding specification.
Summary
We have made clear the basic implementation principle of AsyncTask. At the same time, we also introduced a small skill to pay attention to when using AsyncTask. I hope you can gain something after reading it

Welcome to communicate and discuss together!

Tags: Java Android Apache

Posted on Wed, 17 Nov 2021 05:37:15 -0500 by Thatsmej