Android的线程和线程池
Android中的线程形态
AsyncTask
AsyncTask是一种轻量级的异步任务,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程,并在主线程中更新UI。
不适合进行特别耗时的后台任务
AsyncTask是一个抽象的泛型类,包含Params,Progress,Result三个参数
提供四个核心方法:
onPreExecute:用于做准备工作
doInBackground(Params…params)用于执行异步任务,可以通过publishProgress方法来更新任务进度,publishProgress会调用onProgressUpdate方法
onProgressUpdate(Progress…values):执行进度发生改变会调用
onPostExecute(Result result):result即后台任务的返回值
外加onCancelled方法。
以上方法都是在主线程里执行,除了doInBackground是在子线程。
使用过程需要注意的点:
- AsyncTask的类必须在主线程中加载。
- AsyncTask的对象必须在主线程中创建。
- execute方法必须要在主线程中调用。
- 不要在程序中直接调用onPreExecute,doInBackground,onProgressUpdate,onPostExecute四个方法
- 一个AysncTask对象只能执行一次,即只能调用一次execute,否则会报错。
- 在Android1.6之前,AysncTask是串行执行任务,在Android1.6的时候AysncTask开始采用线程池处理并行任务,但是从Android3.0开始,又回归到串行执行任务。如果需要你并行的话,可以调用executeOnExecutor方法来并行地执行任务。
- AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正地执行任务。InternalHandler用于将执行环境从线程池切换到主线程。
使用例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private TextView statusTextView;
private ProgressBar progressBar;
private Button startButton;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 假设这里绑定了布局文件 setContentView(R.layout.activity_main);
statusTextView = findViewById(R.id.statusTextView);
progressBar = findViewById(R.id.progressBar);
startButton = findViewById(R.id.startButton);
// 点击按钮执行异步任务
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// 实例化任务并执行,不需要传入参数,所以为空
new SimulateLoadDataTask().execute();
}
});
}
/**
* 自定义 AsyncTask
* 泛型参数说明:
* 1. Void: 不需要传入参数给后台任务
* 2. Integer: 使用整数来表示进度
* 3. String: 后台任务执行完返回一个字符串结果
*/
private class SimulateLoadDataTask extends AsyncTask<Void, Integer, String> {
// 【运行在主线程】任务开始前的准备工作
protected void onPreExecute() {
super.onPreExecute();
startButton.setEnabled(false); // 禁用按钮,防止重复点击
statusTextView.setText("准备开始加载...");
progressBar.setProgress(0);
}
// 【运行在子线程】执行真正的耗时操作
protected String doInBackground(Void... voids) {
int totalSteps = 10;
for (int i = 1; i <= totalSteps; i++) {
try {
// 模拟网络请求或文件处理的耗时操作 (每次停顿0.5秒)
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
return "加载被中断";
}
// 计算当前进度百分比,并触发 onProgressUpdate
int progress = (int) ((i / (float) totalSteps) * 100);
publishProgress(progress);
}
return "数据加载成功!"; // 这个返回值会传递给 onPostExecute
}
// 【运行在主线程】更新 UI 进度
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// values[0] 就是我们在 publishProgress 中传入的 progress
progressBar.setProgress(values[0]);
statusTextView.setText("当前进度: " + values[0] + "%");
}
// 【运行在主线程】任务执行完毕后的收尾工作
protected void onPostExecute(String result) {
super.onPostExecute(result);
// 显示后台返回的结果
statusTextView.setText(result);
startButton.setEnabled(true); // 重新启用按钮
}
}
}HandlerThread
是一种可以使用Handler的Thread、
特点:
- 单线程串行执行: 发送给它的所有任务(Message 或 Runnable)都会被放入内部的消息队列中,然后逐个按顺序在后台线程中执行,不会引起并发冲突。
- 避免 UI 线程卡顿: 它可以用来处理轻量级的后台耗时任务(如文件读写、简单的数据库操作),从而释放主线程。
- 安全获取 Looper: 它内部使用了
wait/notifyAll机制,确保只有在Looper初始化完成后,外部才能通过getLooper()获取到实例,避免了空指针异常。
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 1. 创建 HandlerThread 实例,传入线程名称(方便调试)
HandlerThread handlerThread = new HandlerThread("MyBackgroundThread");
// 2. 启动线程(必须先调用 start,内部才会初始化 Looper)
handlerThread.start();
// 3. 使用该线程的 Looper 创建一个 Handler
Handler backgroundHandler = new Handler(handlerThread.getLooper()) {
public void handleMessage(Message msg) {
// 这里的方法运行在子线程,可以执行耗时操作
switch (msg.what) {
case 1:
// 处理任务 1
break;
}
}
};
// 发送任务到子线程
backgroundHandler.sendEmptyMessage(1);
// 或者直接 post Runnable
backgroundHandler.post(() -> {
// 耗时操作
});
IntentService
封装了HandlerThread和Handler,当任务执行完毕后IntentService会自动退出。后台服务优先级高于后台线程,能尽量保证任务执行。
IntentService的OnHandleIntent方法是一个抽象的方法,它需要在子类中实现,它的作用是从Intent参数中区分具体的任务并执行这些任务。
每启动一次IntentService(调用startService方法),就会执行一次onHandlerIntent()。如果目前存在多个后台任务,那么当onHandleIntent方法执行完最后一个任务时,stopSelf(int startId)才会直接停止服务。
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class MySimpleIntentService extends IntentService {
// 必须提供一个无参构造函数,并调用父类传入线程名称(方便调试)
public MySimpleIntentService() {
super("MyBackgroundWorkerThread");
}
protected void onHandleIntent(Intent intent) {
// 【核心】这里的代码已经在底层的 HandlerThread 中运行了!
// 可以直接写耗时操作,绝对不会阻塞主线程。
if (intent != null) {
String taskName = intent.getStringExtra("TASK_NAME");
Log.d("IntentService", "开始处理任务: " + taskName + ",当前线程: " + Thread.currentThread().getName());
try {
// 模拟耗时操作,比如下载文件耗时 3 秒
Thread.sleep(3000);
if("下载图片A".equals(taskName)){
}else if("下载视频B".equals(taskName)){
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("IntentService", "任务完成: " + taskName);
}
}
public void onDestroy() {
super.onDestroy();
Log.d("IntentService", "所有任务执行完毕,服务自动销毁!");
}
}
// 假设我们在 MainActivity 的某个按钮点击事件中启动它
// 规范写法:每次分配新任务,都创建新的 Intent 快递盒
// 任务1:下载图片A
Intent intent1 = new Intent(this, MySimpleIntentService.class);
intent1.putExtra("TASK_NAME", "下载图片A");
startService(intent1);
// 任务2:下载视频B
Intent intent2 = new Intent(this, MySimpleIntentService.class);
intent2.putExtra("TASK_NAME", "下载视频B");
startService(intent2);Android中的线程池
- 线程池的优点:
- 复用线程池中的线程,避免了因为线程的创建和销毁所带来的性能开销。
- 能有效控制线程池的最大并发数,避免大量线程之间因互相抢占资源而导致的阻塞现象
- 线程池的优点:
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Auroraの世界!




