Android中的线程形态

AsyncTask

  • AsyncTask是一种轻量级的异步任务,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程,并在主线程中更新UI。

  • 不适合进行特别耗时的后台任务

  • AsyncTask是一个抽象的泛型类,包含Params,Progress,Result三个参数

  • 提供四个核心方法:

    1. onPreExecute:用于做准备工作

    2. doInBackground(Params…params)用于执行异步任务,可以通过publishProgress方法来更新任务进度,publishProgress会调用onProgressUpdate方法

    3. onProgressUpdate(Progress…values):执行进度发生改变会调用

    4. onPostExecute(Result result):result即后台任务的返回值

      外加onCancelled方法。

      以上方法都是在主线程里执行,除了doInBackground是在子线程。

  • 使用过程需要注意的点:

    1. AsyncTask的类必须在主线程中加载。
    2. AsyncTask的对象必须在主线程中创建。
    3. execute方法必须要在主线程中调用。
    4. 不要在程序中直接调用onPreExecute,doInBackground,onProgressUpdate,onPostExecute四个方法
    5. 一个AysncTask对象只能执行一次,即只能调用一次execute,否则会报错。
    6. 在Android1.6之前,AysncTask是串行执行任务,在Android1.6的时候AysncTask开始采用线程池处理并行任务,但是从Android3.0开始,又回归到串行执行任务。如果需要你并行的话,可以调用executeOnExecutor方法来并行地执行任务。
    7. 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
    90
    import 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;

    @Override
    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() {
    @Override
    public void onClick(View v) {
    // 实例化任务并执行,不需要传入参数,所以为空
    new SimulateLoadDataTask().execute();
    }
    });
    }

    /**
    * 自定义 AsyncTask
    * 泛型参数说明:
    * 1. Void: 不需要传入参数给后台任务
    * 2. Integer: 使用整数来表示进度
    * 3. String: 后台任务执行完返回一个字符串结果
    */
    private class SimulateLoadDataTask extends AsyncTask<Void, Integer, String> {

    // 【运行在主线程】任务开始前的准备工作
    @Override
    protected void onPreExecute() {
    super.onPreExecute();
    startButton.setEnabled(false); // 禁用按钮,防止重复点击
    statusTextView.setText("准备开始加载...");
    progressBar.setProgress(0);
    }

    // 【运行在子线程】执行真正的耗时操作
    @Override
    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 进度
    @Override
    protected void onProgressUpdate(Integer... values) {
    super.onProgressUpdate(values);
    // values[0] 就是我们在 publishProgress 中传入的 progress
    progressBar.setProgress(values[0]);
    statusTextView.setText("当前进度: " + values[0] + "%");
    }

    // 【运行在主线程】任务执行完毕后的收尾工作
    @Override
    protected void onPostExecute(String result) {
    super.onPostExecute(result);
    // 显示后台返回的结果
    statusTextView.setText(result);
    startButton.setEnabled(true); // 重新启用按钮
    }
    }
    }

    HandlerThread

    • 是一种可以使用Handler的Thread、

    • 特点:

      1. 单线程串行执行: 发送给它的所有任务(Message 或 Runnable)都会被放入内部的消息队列中,然后逐个按顺序在后台线程中执行,不会引起并发冲突。
      2. 避免 UI 线程卡顿: 它可以用来处理轻量级的后台耗时任务(如文件读写、简单的数据库操作),从而释放主线程。
      3. 安全获取 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()) {
      @Override
      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
    58
    import android.app.IntentService;
    import android.content.Intent;
    import android.util.Log;

    public class MySimpleIntentService extends IntentService {

    // 必须提供一个无参构造函数,并调用父类传入线程名称(方便调试)
    public MySimpleIntentService() {
    super("MyBackgroundWorkerThread");
    }

    @Override
    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);
    }
    }

    @Override
    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中的线程池

    • 线程池的优点:
      1. 复用线程池中的线程,避免了因为线程的创建和销毁所带来的性能开销。
      2. 能有效控制线程池的最大并发数,避免大量线程之间因互相抢占资源而导致的阻塞现象