1. Basic thread (Thread)
principle
Create and start threads directly through Java's Thread class, suitable for simple asynchronous tasks.
Example
new Thread(new Runnable() { @Override public void run() { // Child threads perform time-consuming tasks (such as network requests) // Note: The UI cannot be updated directly here! runOnUiThread(() -> { ("Task Complete"); // Switch to main thread update UI }); } }).start(); // Kotlin simplified writingThread { // Child thread task runOnUiThread { = "Task Complete" } }.start()
shortcoming
Manual management is complicated: it is difficult to control when there are too many threads.
Unable to update UI directly: The main thread must be switched back to the main thread through runOnUiThread or Handler.
2. Handler and Looper
principle
Inter-thread communication is achieved through Handler and Looper, suitable for scenarios where UI needs to be updated frequently on the main thread.
Example
// Main thread creates HandlerHandler mainHandler = new Handler(()); new Thread(() -> { // The child thread executes the task (() -> { ("Update the UI through Handler"); }); }).start();
Extension: child thread creates message loop
// Child thread initialization Looperclass WorkerThread extends Thread { private Handler workerHandler; @Override public void run() { (); // Create Looper workerHandler = new Handler(()) { @Override public void handleMessage(Message msg) { // Process the messages received by the child thread } }; (); // Start message loop } }
advantage
Flexible control of thread communication: supports delayed message and message queue management.
Main thread safety update UI.
3. AsyncTask (deprecated, only for understanding)
principle
Asynchronous task tools provided by Android in the early days encapsulate thread switching logic internally.
Example
private class MyAsyncTask extends AsyncTask<Void, Integer, String> { @Override protected String doInBackground(Void... voids) { // Child threads perform time-consuming tasks publishProgress(50); // Update progress return "result"; } @Override protected void onProgressUpdate(Integer... values) { // Main thread update progress bar (values[0]); } @Override protected void onPostExecute(String result) { // Main thread processing results (result); } } // Start the tasknew MyAsyncTask().execute();
shortcoming
Memory leak risk: If AsyncTask holds an Activity reference, it may not be recycled.
API 30+ Deprecated: Coroutines or ExecutorService are recommended.
4. ExecutorService (thread pool)
principle
The thread pool management provided by the Java concurrency framework is suitable for scenarios where the number of concurrency needs to be controlled.
Example
// Create a fixed-size thread poolExecutorService executor = (4); (() -> { // The child thread executes the task runOnUiThread(() -> ("Task Complete")); }); // Close the thread pool (usually called in onDestroy)();
advantage
Resource reuse: Avoid the overhead of frequent creation/destruction of threads.
Task Queue Management: Supports submission of Runnable or Callable tasks.
5. IntentService (deprecated, WorkManager is recommended)
principle
Inherited from Service, it handles asynchronous tasks internally through HandlerThread, which is suitable for the background to execute independent tasks.
Example
public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { // The child thread executes tasks (such as file download) // No need to stop manually, it will be automatically destroyed after the task is completed } } // Start the serviceIntent intent = new Intent(context, ); startService(intent);
shortcoming
Android 8.0+ restricted background services: You need to use WorkManager or JobScheduler instead.
6. Kotlin coroutines (Coroutines, modern recommended solutions)
principle
Non-blocking asynchronous operations are implemented through Suspend Functions, simplifying callback hell.
Example
// Use coroutines in ViewModelclass MyViewModel : ViewModel() { fun fetchData() { () { // Switch to IO thread val result = () // Network request withContext() { // Switch back to main thread = result } } } } // Concurrent task processing { val deferred1 = async { fetchData1() } // Start asynchronous task 1 val deferred2 = async { fetchData2() } // Start asynchronous task 2 val result1 = () // Wait for task 1 to complete val result2 = () // Wait for task 2 to complete showResult(result1 + result2) // Merge results}
advantage
- Concise code: use synchronous writing to implement asynchronous logic.
- Lifecycle Awareness: Automatically bind to the ViewModel or Activity lifecycle.
- Flexible scheduling: Specify threads through /IO/Default.
7. HandlerThread
principle
Combined with Thread and Looper, it is suitable for child thread tasks that require long-running.
Example
HandlerThread handlerThread = new HandlerThread("MyHandlerThread"); (); Handler handler = new Handler(()); (() -> { // Execute tasks in HandlerThread}); // Release resources during destruction();
Comparative summary
Way | Applicable scenarios | advantage | shortcoming |
---|---|---|---|
Thread | Simple asynchronous tasks | Direct and easy to use | Manual management is complicated and cannot be updated directly. |
Handler | Main thread communication | Flexible control of message queues | Code redundancy |
AsyncTask | Old project simple task (deprecated) | Automatic thread switching | Memory leak risk, API abandonment |
Executor | Thread pool management | Resource reuse, task queue management | Need to manually switch main thread |
IntentService | Background Independent Task (deprecated) | Automatic destruction | Due to system limitations, the alternative is better |
Coroutine | Modern asynchronous programming | Concise code, life cycle perception | Need to learn Kotlin grammar |
HandlerThread | Child thread tasks that require Looper | Bring your own message loop | Need to exit manually |
Best Practice Recommendations
Simple task: Use Thread + Handler or runOnUiThread.
Complex concurrency: preferred coroutines (with viewModelScope or lifecycleScope).
Thread pool management: Use ExecutorService to control the number of concurrency.
Backend persistent tasks: Use WorkManager (compatible with different API versions).
By rationally choosing a multi-threading solution, the application's response speed and user experience can be significantly improved, while avoiding ANR (Application Not Responding) problems.
This is the article about several ways to implement multi-threading in Android. For more related content on Android implementation, please search for my previous articles or continue browsing the related articles below. I hope you will support me in the future!