0%

Android 的线程和线程池(二):Android 中的线程形态

1. AsyncTask 概述

  • 官方已不再推荐使用,API 30 源码注释:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * bla bla bla
    *
    * @deprecated Use the standard <code>java.util.concurrent</code> or
    * <a href="https://developer.android.com/topic/libraries/architecture/coroutines">
    * Kotlin concurrency utilities</a> instead.
    */
    @Deprecated
    public abstract class AsyncTask<Params, Progress, Result> {
    // bla bla bla
    }
  • AsyncTask 是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程中更新 UI

    • AsyncTask 内部封装了线程池Handler,通过 AsyncTask 可以更加方便地执行后台任务以及在主线程中访问 UI
    • AsyncTask 并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池
  • AsyncTask 是一个抽象的泛型类,提供了 Params、Progress 和 Result 这三个泛型参数

    • Params 表示参数的类型,Progress 表示后台任务的执行进度的类型,Result 表示后台任务的返回结果的类型
    • 如果 AsyncTask 确实不需要传递具体的参数,那么这三个泛型参数可以用 Void 来代替
    • AsyncTask 类的声明为:public abstract class AsyncTask<Params, Progress, Result>
  • AsyncTask 提供了 4 个核心方法,含义如下表:

    核心方法 含义
    onPreExecute() 主线程中执行,在异步任务执行之前,此方法会被调用,一般可以用于做一些准备工作
    doInBackground(Params... params) 线程池中执行,此方法用于执行异步任务,params 参数表示异步任务的输入参数。在此方法中可以通过 publishProgress() 方法来更新任务的进度,publishProgress() 方法会调用 onProgressUpdate() 方法。另外此方法需要返回计算结果给 onPostExecute() 方法
    onProgressUpdate(Progress... values) 主线程中执行,当后台任务的执行进度发生改变时此方法会被调用
    onPostExecute(Result result) 主线程中执行,在异步任务执行之后,此方法会被调用,其中 result 参数是后台任务的返回值,即 doInBackground() 方法的返回值
    • AsyncTask 还提供了 onCancelled() 方法,同样在主线程中执行。当异步任务被取消时,onCancelled() 方法会被调用,此时 onPostExecute() 方法不会被调用
  • Demo

    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
    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");
    }
    }

    // 执行下载任务
    new DownloadFilesTask().execute(url1, url2, url3);
  • AsyncTask 在具体的使用过程中的条件限制

    • AsyncTask 的类必须在主线程中加载,即第一次访问 AsyncTask 必须发生在主线程,这个过程在 Android 4.1 及以上版本中已经被系统自动完成。在 Android 5.0 的源码中 ,可以查看 ActivityThread 的 main() 方法,它会调用 AsyncTask 的 init() 方法,这就满足了 AsyncTask 的类必须在主线程中进行加载这个条件
    • AsyncTask 的对象必须在主线程中创建;execute() 方法必须在主线程中调用
    • 不要在程序中直接调用 onPreExecute()onPostExecute()doInBackground()onProgressUpdate() 方法
    • 一个 AsyncTask 对象只能执行一次,即只能调用一次 execute() 方法,否则会报运行时异常
    • 在 Android 1.6 之前,AsyncTask 是串行执行任务的,Android 1.6 时 AsyncTask 开始采用线程池处理并行任务。但从 Android 3.0 开始,为了避免 AsyncTask 带来的并发错误,AsyncTask 又采用了一个线程来串行执行任务。尽管如此,在 Android 3.0 及后续的版本,我们仍然可以通过 AsyncTask 的 executeOnExecutor() 方法来并行执行任务

2. AsyncTask 工作原理概述

  • AsyncTask 中有两个线程池: SerialExecutorTHREAD_POOL_EXECUTOR 和一个 Handler(InternalHandler)

    • 线程池 SerialExecutor 用于任务的排队,线程池 THREAD_POOL_EXECUTOR 用于真正地执行任务
    • Handler InternalHandler 用于将执行环境从线程池切换到主线程
  • execute(Param... params) 方法源码:

    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
    /**
    * Executes the task with the specified parameters. The task returns
    * itself(this) so that the caller can keep a reference to it.
    *
    * <p>Note: this function schedules the task on a queue for a single background
    * thread or pool of threads depending on the platform version. When first
    * introduced, AsyncTasks were executed serially on a single background thread.
    * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
    * to a pool of threads allowing multiple tasks to operarte in parallel. Starting
    * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
    * executed on a single thread to avoid common application errors caused
    * by paralled execution. If you truly want paralel execution, you can use
    * the {@link #executeOnExecutor} version of this method
    * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
    * on its use.
    *
    * <p>This method must be invoked on the UI thread.
    *
    * @param params The parameters of the task.
    *
    * @return This instance of AsyncTask.
    *
    * @throws IllegalStateException If {@link #getStatus()} return either
    * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
    *
    * @see #execteOnExecutor(java.util.concurrent.Executor, Object[])
    * @see #execute(Runnable)
    */
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executorOnExecutor(sDefaultExecutor, params);
    }

    @UnsupportedAppUsage
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    /**
    * An {@link Executor} that executes tasks one at a time in serial
    * order. This serialization is global to a particular process.
    * d
    * @deprecated Globally serializing tasks results in excessive queuing for unrelated operations.
    */
    @Deprecated
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable> ();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
    mTasks.offer(new Runnable() {
    public void run() {
    try {
    r.run();
    } finally {
    scheduleNext();
    }
    }
    });
    if (mActive == null) {
    scheduleNext();
    }
    }

    protected synchronized void scheduleNext() {
    if ((mActive = mTask.poll() != null)) {
    THREAD_POOL_EXECUTOR.executor(mActive);
    }
    }
    }
    • sDefaultExecutor 是一个串行的线程池,一个进程中所有的 AsyncTask 全部在这个串行的线程池中排队执行
    • 从 SerialExecutor 的实现可以分析 AsyncTask 的排队执行的过程。从 Android 3.0 开始,默认情况下 AsyncTask 是串行执行的
  • executeOnExecutor(Executor exec, Params... params) 方法源码:

    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
    /**
    * Executes the task with the specified parameter. The task returns
    * itself(this) so that the caller can keep a reference to it.
    *
    * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
    * allow multiple tasks to run in parallel on a pool of threads managed by
    * AsyncTask, however you can also use your own {@link Executor} for custom
    * behavior.
    *
    * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
    * a thread pool is generally <em>not</em> what one wants, because the order
    * of their operation is not defined. For example, if these tasks are used
    * to modify any state in common (such as writing a file due to a button click),
    * there are no guarantees on the order of the modifications.
    * Without careful work it is possible in rare cases for the newer version
    * of the data to be over-written by an older one, leading to obscure data
    * loss and stability issues. Such changes are best
    * executed in serial; to guarantee such work is serialized regardless of
    * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
    *
    * <p>This method must be invoked on the UI thread.
    *
    * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a
    * convenient process-wide thread pool for tasks that are loosely coupled.
    *
    * @param params The parammeter of the task.
    *
    * @return This instance of AsyncTask.
    *
    * @throw IllegalStateException If {@link #getStatus()} returns either
    * {@link AsyncTask.Status@RUNNING} or {@link AsyncTask.Status#FINISHED}.
    *
    * @see @execute(Object[])
    */
    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
    if (mStatus != Status.PENDING) {
    switch (mStatus) {
    case RUNNING:
    throw new IllegalStateException("Cannot execte task: " + "the task is already running.");
    case FINISHED:
    throw new IllegalStateException("Canot execute task: " + "the task has already been executed " + "(a task can be executed only once)");
    }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execte(mFuture);

    return this;
    }
    • executeOnExecutor() 方法中,AsyncTask 的 onPreExecute() 方法最先执行,然后线程池开始执行
  • 构造方法 AsyncTask(Looper) 源码:

    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
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    private static InternalHandler sHandler;

    @UnsupportedAppUsage
    private final WorkerRunnable<Params, Result> mWorker;
    @UnsupportedAppUsage
    private final FutureTask<Result> mFuture;

    private final Handler mHandler;

    /**
    * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
    *
    * @hide
    */
    public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
    ? getMainHandler() : new Handler(callbackLooper);

    mWorker = new WorkerRunnable<Params, Result> () {
    public Result call() throws Exception {
    mTaskInvoked.set(true);
    Result result = null;
    try {
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    // noinspection unchecked
    result = doInBackground(mParams);
    Binder.flushPendingCommands();
    } catch (Throwable tr) {
    mCancelled.set(true);
    throw tr;
    } finally {
    postResult(result);
    }
    return result;
    }
    };

    mFuture = new FutureTask<Result>(mWorker) {
    @Override
    protected void done() {
    try {
    postResultIfNotInvoked(get());
    } catch (InterruptedException e) {
    android.util.Log.w(LOG.TAG, e);
    } catch (ExecutionException e) {
    throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause());
    } catch (CancellationException e) {
    postResultIfNotInvoked(null);
    }
    }
    };
    }

    private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
    if (sHandler == null) {
    sHandler = new InternalHandler(Looper.getMainLooper());
    }
    return sHandler;
    }
    }

    // 私有的静态内部类
    private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
    super(looper);
    }

    @SuppressWarnings({"unchecked", "RawUseOfParamterizedType"})
    @Override
    public void handleMessage() {
    AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
    switch (msg.what) {
    case MESSAGE_POST_RESULT:
    // There is only one result
    result.mTask.finish(result.mData[0]);
    break;
    case MESSAGE_POST_PROGRESS:
    result.mTask.onProgressUpdate(result.mData);
    break;
    }
    }
    }

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
    final AsyncTask = mTask;
    final Data[] mData;

    AsyncTaskResult(AsyncTask task, Data... data) {
    mTask = task;
    mData = data;
    }
    }

    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
    }

    private Handler getHandler() {
    return mHandler;
    }

    private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
    }

    private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
    postResult(result);
    }
    }
    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
    package java.util.concurrent;

    /**
    * A task that returns a result and may throw an exception.
    * Implementors define a single method with no arguments called
    * {@code call}.
    *
    * <p>The {@code Callable} interface is similar to {@link
    * java.lang.Runnable}, in that both are designed for classes whose
    * instances are potentially executed by another thread. A
    * {@code Runnable}, however, does not return a result and cannot
    * throw a checked exception.
    *
    * <p>The {@link Executors} class contains utility methods to
    * convert from other common forms to {@code Callable} clases.
    *
    * @see Executor
    * @since 1.5
    * @author Doug Lea
    * @param <V> the result type of method {@code call}
    */
    @FunctionalInterface
    public interface Callable<V> {
    /**
    * Computes a result, or throws an exception if unable to do so.
    *
    * @return computed result
    * @throws Exception if unable to compute a result
    */
    V call() throws Exception;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package java.util.concurrent;

    /**
    * Creates a {@code FutureTask} that will, upon running, execute the
    * given {@code Callable}.
    *
    * @param callable the callable task
    * @throws NullpointerException if the callable is null
    */
    public FutureTask(Callable<V> callable) {
    if (callable == null)
    throw new NullPointerException();
    this.callable = callable;
    this.state = NEW; // ensure visibility of callable
    }
    • sHandler 是一个静态的 Handler 对象,为了能够将执行环境切换到主线程,这就要求 sHandler 这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求 AsyncTask 的类必须在主线程中加载,否则同一个进程中的 AsyncTask 都将无法正常工作

3. HandlerThread 概述

  • HandlerThread 继承了 Thread,是一种可以使用 Handler 的 Thread。HandlerThread 源码:

    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
    package android.os;

    import android.annotation.NonNull;
    import android.annotation.Nullable;

    /**
    * A {@link Thread} that has a {@link Looper}.
    * The {@link Looper} can then be used to create {@link Handler}s.
    * <p>
    * Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
    */
    public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
    super(name);
    mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
    * This method returns the Looper associated with this thread. If this thread not been started
    * or for any reason isAlive() returns false, this method will return null. If this thread
    * has been started, this method will block until the looper has been initialized.
    * @return The looper.
    */
    pubic Looper getLooper() {
    if (!isAlive()) {
    return null;
    }

    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
    while (isAlive() && mLooper == null) {
    try {
    wait();
    } catch (InterruptedException e) {
    }
    }
    }
    return mLooper;
    }

    // bla bla bla
    }
    • 从类注释可知,HandlerThread 的 start() 方法仍然需要手动调用
    • HandlerThread 和普通的 Thread 有显著区别:普通 Thread 主要用于在 run() 方法中执行一个耗时任务;HandlerThread 在内部创建了消息队列,外界需要通过 Handler 的消息方式来通知 HandlerThread 执行一个具体的任务
  • HandlerThread 的 run() 方法源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Override
    public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
    mLooper = Looper.myLooper();
    notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
    }

    /**
    * Call back method that can be explicitly overridden if needed to execute some
    * setup before Looper loops.
    */
    protected void onLooperPrepared() {
    }
    • 实现很简单,就是在 run() 方法中通过 Looper.prepare() 来创建消息队列,并通过 Looper.loop() 来开启消息循环,这样在实际使用中就允许在 HandlerThread 中创建 Handler
    • 因为 Looper.loop() 方法是一个无限循环,所以 HandlerThread 的 run() 方法也是一个无限循环。因此当明确不需要再使用 HandlerThread 时,可通过调用 HanlderThread 的 quit()quitSafely() 方法来终止线程的执行
  • HandlerThread 是一个很有用的类,在 Android 中的一个具体使用场景是 IntentService

4. IntentService 概述

  • Android 8.0 以下版本官方已不推荐使用,API 30 源码注释:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * bla bla bla
    *
    * @deprecated IntentService is subject to all the
    * <a href="/preview/features/background.html">background execution limits</a>
    * imposed with Android 8.0 (API level 26). Consider using {@link androidx.work.WorkManager}
    * or {@link androidx.core.app.JobIntentService}, which uses jobs
    * instead of services when running on Android 8.0 or higher.
    */
    @Deprecated
    public abstract class IntentService extends Service {
    // bla bla bla
    }
  • IntentService 继承了 Service 并且是一个抽象类,因此必须创建它的子类才能使用 IntentService

  • IntentService 可用于执行后台耗时的任务,当任务执行完成后会自动停止。同时由于 IntentService 是服务的原因,所以优先级比单纯的线程高很多,所以 IntentService 比较适合执行一些高优先级的后台任务

  • 在实现上,IntentService 封装了 HandlerThread 和 HandleronCreate() 方法源码:

    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
    private volatile Looper mServiceLooper;

    @UnsupportedAppUsage
    private volatile ServiceHandler mServiceHandler;

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

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

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

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
    • 当 IntentService 被第一次启动时,它的 onCreate() 方法会被调用onCreate() 方法会创建一个 HandlerThread,然后使用它的 Looper 来构造一个 Handler 对象 mServiceHandler。这样通过 mServiceHandler 发送的消息最终都会在 HandlerThread 中执行,从这个角度看,IntentService 也可以用于执行后台任务
    • onHandleIntent() 是一个抽象方法,需要在子类中实现,作用是从 Intent 参数中区分具体的任务并执行
  • 每次启动 IntentService,它的 onStartCommand() 方法就会调用一次,IntentService 在 onStartCommand() 方法中处理每个后台任务的 IntentonStartCommand() 方法源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private boolean mRedelivery;

    /**
    * You should not override this method for your IntentService. Instead,
    * override {@link #onHandleIntent}, which the system calls when the IntentService
    * receives a start request.
    * @see android.app.Service#onStartCommand
    */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
    }
    • IntentService 仅仅是通过 mServiceHandler 发送了一个消息,这个消息会在 HandlerThread 中被处理。mServiceHandler 收到消息后,会将 Intent 对象传递给 onHandleIntent() 方法处理
    • onHandleIntent() 方法执行结束后,IntentService 会通过 stopSelf(int startId) 方法来尝试停止服务。stopSelf(int startId) 方法会等待所有的消息都处理完才终止服务,stopSelf() 方法会立刻停止服务(有点类似 quit()quitSafely() 的区别),服务停止是会回调 onDestroy() 方法
    • 由于每执行一个后台任务就必须启动一次 IntentService,而 IntentService 内部通过消息的方式向 HandlerThread 请求执行任务,Handler 中的 Looper 是顺序处理消息的,这就意味着 IntentService 也是顺序执行后台任务的,即当有多个后台任务同时存在时,这些后台任务会按照外界发起的顺序排队执行
-------------------- 本文结束感谢您的阅读 --------------------