Android AsyncTask

  • 2020-06-12 10:36:03
  • OfStack

AsyncTask is a very common API, especially where data is processed asynchronously and applied to views. AsyncTask isn't all that good. It's even a little bad. In this article, I'll talk about what problems AsyncTask causes, how to fix them, and some alternatives to AsyncTask.

AsyncTask

Starting with Android API 3 (1.5 Cupcake), AsyncTask was introduced to help developers manage threads more easily. There is actually a similar implementation in Android 1.0 and 1.1, which is UserTask. UserTask and AsyncTask share the same API and implementation, but since the device share of 1.0 and 1.1 is small, UserTask will not be covered here.

The life cycle

There is one such widespread misconception about AsyncTask, with many believing that AsyncTask, one in Activity, will be destroyed when Activity is destroyed. But it's not. AsyncTask executes the doInBackground() method until the end of execution. Once the above method is finished, different operations will be performed according to the situation.

1. If cancel(boolean) is called, the onCancelled(Result) method is executed
2. If cancel(boolean) is not called, the onPostExecute(Result) method is executed

The cancel method of AsyncTask requires a Boolean parameter named mayInterruptIfRunning to indicate whether execution can be interrupted if it is in progress, and if this value is set to true to indicate that the task can be interrupted, otherwise the executing program will continue until it is completed. If there is a loop operation in the doInBackground() method, we should use isCancelled() in the loop to determine, and if the return is true, we should avoid performing subsequent useless loop operations.

In summary, using AsyncTask we need to make sure that AsyncTask cancels correctly.

Not working well cancel()

The short answer is that sometimes it works.

If you call cancel(false) of AsyncTask, doInBackground() will still execute to the end of the method, just not call onPostExecute(). But in reality this is making the application perform meaningless operations. So will the problem before we call cancel(true) be solved? Not really. If mayInterruptIfRunning is set to true, the task will end as soon as possible, but if doInBackground() has an uninterruptible method, such as this ES72en.decodeStream () operation. But you can close the IO stream ahead of time and catch exceptions thrown by such an operation. But this would make the cancel() method meaningless.

Memory leaks

Another common situation is to use a non-static anonymous inner AsyncTask class in Activity, where, due to the nature of the Java inner class, the AsyncTask inner class holds an implicit reference to the outer class. As the life cycle of AsyncTask may be longer than that of Activity, when Activity destroys AsyncTask, AsyncTask holds a reference to Activity, resulting in the memory leak.

Results the loss

Another problem is the loss of AsyncTask data when the Activity is recreated due to screen rotation and so on. When Activity is destroyed and innovatively created, the still running AsyncTask holds one illegal reference to Activity, the previous instance of Activity. Causes onPostExecute() to have no effect.

Serial or parallel

There are a lot of questions about whether the AsyncTask is serial or parallel, which is normal because it has been modified many times. If you don't know what is serial or parallel, just look at the following example. Suppose we have the following two lines of code in the body of a method


new AsyncTask1().execute();
new AsyncTask2().execute();

Should the above two tasks be executed at the same time, or should AsyncTask2 not be executed until AsyncTask1 has completed its execution? It turns out that the results are different depending on API.

Before 1.6(Donut) :

In AsyncTask version 1, the task was serial scheduling. One task is executed until the other one is completed. Using more than one AsyncTask can be problematic because of the serial execution of tasks. So this is not a good way to handle asynchronous (especially if you need to apply the result to an UI attempt) operation.

From 1.6 to 2.3 (Gingerbread)

Later, the Android team decided to use AsyncTask in parallel to solve the problem caused before 1.6. This problem was solved and a new problem appeared. Many developers actually rely on sequential behavior. So a lot of concurrency problems come up.

3.0 (Honeycomb) to date

Well, developers might not like to have AsyncTask parallel, so the Android team changed AsyncTask to serial. Of course, this change does not completely prohibit AsyncTask parallelism. You can achieve multiple AsyncTask parallelism by setting executeOnExecutor(Executor). The API documentation is described below


If we want to make sure we have control over the execution, whether it will run serially or parallel, we can check at runtime with this code to make sure it runs parallel:


public static void execute(AsyncTask as) {
  if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR1) {
      as.execute();
  } else {
      as.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }
}
//(This code does not work for API lvl 1 to 3)

Do you really need AsyncTask

No, AsyncTask can be used asynchronously in short code, but as mentioned in this article, there are a lot of rules to be careful about if you want AsyncTask to work properly. One of the recommended techniques for asynchronous operations is to use Loaders. This method was introduced with Android 3.0 (Honeycomb) and is included in the android support package. You can learn more about Loaders by viewing the official documentation.

In this translation, a few parts of the original text are deleted and modified.


Related articles: