Detailed explanation of 6 implementations of Android asynchronous tasks

Detailed explanation of 6 implementations of Android asynchronous tasks


The Android UI thread (main thread) has several characteristics:

  • The UI view can only be operated on the UI thread, not in sub-threads.
  • Time-consuming operations cannot be performed in the UI thread, otherwise the UI thread will be blocked, causing problems such as ANR and freeze.

In Android development, we usually use asynchronous tasks to process some time-consuming operations. For example, in such a scenario, the child thread executes an asynchronous task in the background. During the task process, the UI process needs to display the progress. At this time, we need a tool to realize this requirement. The Android system provides developers with an asynchronous task class (AsyncTask) to implement the above-mentioned functions, that is, it will perform computing tasks in a sub-thread, and at the same time obtain the opportunity to update the application interface through the message loop of the main thread.

What are the commonly used asynchronous tasks in Android development?

1. Created using Thread

The most direct way is to use the Thread class provided by Java to create threads to achieve asynchrony.

For how to create Thread, please refer to "Summary of Three Ways to Create Threads in Java" .

2. Thread + Looper + handler

Android provides a Handler mechanism to communicate between threads, we can use Android's most basic asynchronous method: Thread + Looper + handler to perform asynchronous tasks.

For the principles of Handler-related mechanisms, please refer to: "Handler Thread Communication Mechanism: Actual Combat, Principles, and Performance Optimization!" "

Sample code:

Handler mHandler = newHandler(){
    @Override
    publicvoid handleMessage(Message msg){
        if(msg.what == 1){
            textView.setText("Task Done!!");
        }
    }
};
mRunnable = new Runnable() {
    @Override
    publicvoid run() {
        SystemClock.sleep(1000);    // 耗时处理
        mHandler.sendEmptyMessage(1);  
    }
};
private void startTask(){
    new Thread(mRunnable).start();
}

advantage:

  • The operation is simple and there is no learning cost.

shortcoming:

  • The code is poorly standardized and difficult to maintain.
  • Each operation will open an anonymous thread, and the system overhead is large.

3. AsyncTask

A relatively lightweight asynchronous class that encapsulates FutureTask's thread pool, ArrayDeque and Handler for scheduling. AsyncTask is mainly used for continuous interaction between the background and the interface.

Let's take a look at the definition of the abstract class AsyncTask. When we define a class to inherit the AsyncTask class, we need to specify three generic parameters for it:

AsyncTask <Params, Progress, Result>

  • Params: This generic specifies the type of parameters we pass to the asynchronous task execution.
  • Progress: This generic type specifies the type of parameter that our asynchronous task will return to the UI thread during execution.
  • Result: The type of the result returned to the UI thread after the asynchronous task specified by this generic type is executed.

When we define a class that inherits the AsyncTask class, we must specify the types of these three generic types. If they are not specified, they will all be written as void.

Let's look at an official example:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }
     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }
     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
}

When using it, you only need to integrate AsyncTask, create an object and call execute to execute:

new DownloadFilesTask().execute(url1, url2, url3);

Execute time-consuming logic in the doInBackground(Params…) method, and then update the result back to the UI component in onPostExecute(Result)

Among the main methods of AsyncTask, the doInBackground method runs on the child thread, and the execute, onPreExecute, onProgressUpdate, and onPostExecute methods all run on the UI thread.

Notes on using AsyncTask

  • The instance of AsyncTask must be created in UI Thread.
  • The execute method of an AsyncTask can only be called from the UI thread.
  • The four overridden methods of AsyncTask are called automatically by the system and should not be called manually.
  • Each AsyncTask can only be executed once, multiple executions will cause an exception.
  • Of the four methods of AsyncTask, only the doInBackground method runs in other threads, and the other three methods run in the UI thread, which means that the other three methods can perform UI update operations.
  • AsyncTask is executed serially by default. If parallel execution is required, use the interface executeOnExecutor method.

advantage:

  • Clear structure, easy to use, suitable for background task interaction.
  • The priority of the asynchronous thread has been set by default: THREAD_PRIORITY_BACKGROUND, which will not seize resources with the UI thread.

shortcoming:

  • The structure is slightly complicated and the code is more.
  • Each AsyncTask can only be executed once, and an exception will occur if called multiple times.
  • AsyncTask maintains a thread pool throughout the Android system, which may be preempted by tasks of other processes and reduce efficiency.

Note: For the principle of AsyncTask and more details, please refer to "Do you really know how to use AsyncTask?" Actual combat, principle, best practice! (Android Q)" .

4. HandlerThread

HandlerThread is a thread class with its own Looper message loop. The way to handle asynchronous tasks is the same as Thread + Looper + Handler.

advantage:

  • Simple, the Looper message loop of ordinary threads is implemented internally.
  • Multiple tasks can be executed serially.
  • It has its own message queue inside and will not block the UI thread.

shortcoming:

  • If there is no result returned to the interface, it needs to be handled by itself.
  • When there are too many messages, it is easy to cause blockage.
  • There is only one thread processing, which is less efficient.
  • Thread priority The default priority is THREAD_PRIORITY_DEFAULT, which is easy to preempt resources from the UI thread.

Note: For more details, please refer to: "HandlerThread principle analysis, actual combat, best practice!" " .

5. IntentService

IntentService inherits from the Service class and is used to start an asynchronous service task. It implements asynchronous processing tasks internally through HandlerThread.

Let's look at the main method of IntentService:

    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

advantage:

  • You only need to inherit IntentService, and you can asynchronously process Intent type tasks in the onHandlerIntent method.
  • After the task ends, the IntentService will stop by itself, no need to call stopService manually.
  • It can process multiple Intent requests and execute multiple tasks sequentially.
  • IntentService is inherited from Service and has the priority of background Service.

shortcoming:

  • Need to start the service to perform asynchronous tasks, not suitable for simple task processing.
  • Asynchronous tasks are implemented by HandlerThread, which can only process tasks in a single thread and sequentially.
  • There is no interface back to the UI thread.

6. Use the thread pool to handle asynchronous tasks

Use the static methods newCachedThreadPool(), newFixedThreadPool(), newSingleThreadExecutor() and overloaded forms of Executors to instantiate the ExecutorService interface to get the thread pool object.

  • Dynamic thread pool newCachedThreadPool(): Create new threads according to the needs. When the demand is large, more will be created. When the demand is small, the JVM will slowly release the redundant threads.
  • A fixed number of thread pools newFixedThreadPool(): There is a task blocking queue inside. Suppose there are 2 threads in the thread pool and 4 tasks are submitted, then the last two tasks are placed in the task blocking queue, even if the first 2 tasks sleep Or if it is blocked, the last two tasks will not be executed unless the first two tasks have been executed.
  • Single-threaded newSingleThreadExecutor(): A single-threaded thread pool, this thread pool can restart a thread after the thread dies (or when an exception occurs) to replace the original thread and continue to execute.

advantage:

  • The creation and destruction of threads is maintained by the thread pool, which realizes the reuse of threads, thereby reducing the overhead of thread creation and destruction.
  • It is suitable for performing a large number of asynchronous tasks and improving performance.
  • High flexibility, you can freely control the number of threads.
  • Good scalability, can be expanded according to actual needs.

shortcoming:

  • The code is slightly more complicated.
  • The thread pool itself consumes system resources to a certain extent.
  • When there are too many threads, the cost of switching between threads will be very high, which will seriously degrade performance.
  • Each thread consumes at least 1040KB of memory, and the number of threads in the thread pool needs to be controlled within a certain range.
  • The priority of the thread is inherited. If a thread pool is created in the UI thread, the default priority of the thread will be the same as that of the UI thread, thereby preempting the resources used by the UI thread.

Summarize


This article introduces in detail six ways to implement asynchronous tasks in Android:

  1. Created using Thread
  2. Use Thread + Looper + handler
  3. Use AsyncTask
  4. Use HandlerThread
  5. Use IntentService
  6. Use a thread pool to handle asynchronous tasks

At the same time, we also analyzed the usage scenarios of each method and its advantages and disadvantages.


**PS: For more exciting content, please view --> "Android Development"
**PS: For more exciting content, please view --> "Android Development"
**PS: For more exciting content, please view --> "Android Development"


---------------------
Author: Uncle Bu
Source: CSDN
Original: https://blog.csdn.net/u011578734/article/details/110523825Copyright
statement: This article is the author's original article, please attach the blog post link for reprinting!
Content analysis By: CSDN, CNBLOG blog post one-click reprint plug-in

Guess you like

Origin blog.csdn.net/xiaowang_lj/article/details/131893248