Android 线程相关 | 艺术探索笔记

在 Android 中,除了 Thread 以外,还有 AsyncTask、HandlerThread 和 IntentService 充当线程的作用,它们各自有不同的特点和适用场景。

AsyncTask 封装了线程池和 Handler,它能方便使用者在子线程中更新 UI。HandlerThread 是使用 Handler 的线程。IntentService 是一个服务,它比一般的后台线程优先级更高,不容易被系统杀死,所以可以更方便的执行后台线程。

AsyncTask

AsyncTask 是一个轻量级的异步任务类,它不适合进行特别耗时的任务。

用法

public abstract class AsyncTask

AsyncTask 是一个抽象的泛型类,它提供了 Params、Progress、Result 三个泛型参数。Params 表示参数的类型,Progress 表示后台任务的执行进度,Result 表示后台任务返回结果的类型。如果 AsyncTask 不需要传递参数,以上三个都可以用 Void 来替代。

AsyncTask 有四个核心方法

  1. onPreExecute()
  2. doInBackground(Params... params)
  3. onProgressUpdate(Progress... values)
  4. onPostExecute()

onPreExecute 方法在主线程执行,一般用于准备工作。

doInBackground 方法会在线程池中执行异步任务,并将计算结果返回给 onPostExecute 方法。在该方法中,可以调用 publishProgress 方法来更新 UI。

当调用 publishProgress 方法后,onProgressUpdate 方法会在主线程中执行,它用于进行 UI 操作。

当 doInBackground 方法完成后,onPostExecute 方法将会执行,它也存在于主线程。

需要注意的是,AsyncTask 还存在一个 onCancelled 方法。当异步任务被取消时,它会被调用。该方法一旦被调用就不会再调用 onPostExecute 方法。它也同样是在主线程中执行。

注意事项

  • AsyncTask 必须在主线程中加载
  • AsyncTask 对象必须在主线程中创建
  • 不要在程序中直接使用 onPreExecute、doInBackground、onProgressUpdate、onPostExecute 四个方法
  • 一个 AsyncTask 对象只能执行一次 execute 方法
  • 从 Android 3.0 开始,AsyncTask 采用一个线程串行执行任务,可以使用 executeOnExecutor 方法调整为并行

源码分析

先看 execute 方法

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

public final AsyncTask execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

public final AsyncTask executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
            throw new IllegalStateException("Cannot execute task:"
                + " the task is already running.");
            case FINISHED:
            throw new IllegalStateException("Cannot execute task:"
                + " the task has already been executed "
                + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

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

    return this;
}

execute 方法调用了 executeOnExecutor 方法,在 executeOnExecutor 方法中,onPreExecute 方法首先得到执行,然后是 sDefaultExecute变量的 execute 方法。由于sDefaultExecute是 SerialExecutor 类,于是我们转到该类

private static class SerialExecutor implements Executor {
    final ArrayDeque mTasks = new ArrayDeque();
    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 = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

从 SerialExecutor 中可以看到,execute 方法首先会把 Runnable 对象,也就是传入的mFuture,插入到队列 ArrayDeque 中。当队列头部存在任务时,会调用 FutureTask 类(mFuture)的 run 方法,并通过 scheduleNext 方法获取下一个任务。如果队列头部没有任务,就会直接调用 scheduleNext 方法来获取任务。接着看 FutureTask 的 run 方法

public void run() {

    ...
    
    try {
        Callable c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            ...
        }
    } finally {
        ...
    }
}

可以看到,在这个方法中会调用 callable 的 call 方法,那这个方法是什么?

public AsyncTask() {
    mWorker = new WorkerRunnable() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            return postResult(doInBackground(mParams));
        }
    };

    mFuture = new FutureTask(mWorker) {
        ...
    };
}

public FutureTask(Callable callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

根据这一段代码,再结合前面的关系,execute 方法最终是调用了mWork的 call 方法。在这个方法中,mTaskInvoked被设为true,表示当前任务已经被调用,然后会执行 doInBackground 方法,并将返回值传给 postResult 方法

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

在 postResult 方法中,sHandler 会发送出MESSAGE_POST_RESULT,来看 sHandler

private static final InternalHandler sHandler = new InternalHandler();

private static class InternalHandler extends Handler {
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        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;
        }
    }
}

在这段代码中,sHandler是一个静态的 Handler 对象,而静态成员会在加载类时初始化,为了能切换回主线程,这个sHandler必须在主线程中创建,这就相当于要求 AsyncTask 在主线程中加载。收到MESSAGE_POST_RESULT后会调用 finish 方法,来看

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

如果任务被取消了,将会调用 onCancelled 方法。如果正常执行,会调用 onPostExecute 方法,参数是 doInBackground 方法的结果。

HandlerThread

它是可以使用 Handler 的 Thread,看它的 run 方法

public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

在 run 方法中,通过Looper.prepare()创建消息队列,Looper.loop()开启消息循环。

HandlerThread 和 Handler 一样,使用时需要通过 sendMessage 方法发送消息来处理任务。使用 HandlerThread 时,需要通过 start 开启线程,并实现 handleMessage 方法来完成处理逻辑

HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler workderHandler = new Handler(handlerThread.getLooper()) {
    @Override
    public boolean handleMessage(Message msg) {
        // 处理消息
        return true;
    }
}

当使用完毕后,记得通过 quit 或 quitSafely 方法退出线程循环,避免内存泄漏。

IntentService

IntentService 是一个抽象类,它继承了 Service。使用时需要创建它的子类。IntentService 一般用于执行后台程序,原因是它属于 Service,导致它比一般线程优先级要高,更不容易被杀死。它适用于高优先级的后台任务。

使用方法

必须实现 onHandleIntent 方法以执行后台任务。可根据需求重写 onCreate、onStartCommand、onDestroy 方法。

源码分析

IntentService 内部封装了 Handler 和 HandlerThread。先看它的 onCreate 方法

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

第一次启动 IntentService 会调用它的 onCreate 方法,这个方法中会创建 HandlerThread。

再看 onStartCommand 方法

public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

onStartCommand 方法调用了 onStart 方法,那么转到 onStart

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

在 onStart 方法中,intent被赋给msg.obj,并通过 ServiceHandler 实例发送了出去,ServiceHandler 继承自 Handler,意味着这条消息最终会来到 handleMessage 方法中,于是转到该方法

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

在这里看到,会在 onHandleIntent 方法中进行处理,结合前面的代码可以知道,该方法中的 Intent 对象和通过 startIntent 方法中转入的 Intent 对象是相同的,我们可以通过这个 Intent 对象解析出外界启动 IntentService 时传入的参数,通过这些参数就可区分具体的后台任务。onHandleIntent 是一个抽象方法,需要我们在子类中实现。

stopSelf(mig.arg1)方法用来尝试停止任务。注意区分stoopSelf(int startId)方法和stopSelf()方法,前者会等所有消息处理完之后才停止服务,而后者会立刻停止服务。

线程池

线程池有以下几个优点:

  • 重用线程池中的线程,减少线程创建和销毁的性能开销
  • 控制最大线程数,避免因线程过多而抢占系统资源导致的阻塞
  • 能够对进程进行管理

Android 中的线程池来自 Java 的 Executor,它是一个接口,真正的实现是 ThreadPoolExecutor。

ThreadPoolExecutor

基本介绍

它的构造方法提供了一系列参数来配置线程池

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}
  • corePoolSize

线程池的核心线程数,默认情况下,核心线程会一直在线程池里存活。

  • maximumPoolSize

最大线程数,当活动线程达到最大值时,后续的任务会被阻塞。

  • keepAliveTime

非核心线程存活时间,当非核心线程闲置时间超过它时,就会被回收。当 ThreadPoolExecutor 的 allowCoreThreadTimeOut 设置为true时,keepAliveTime 同样会作用于核心线程。

  • unit

keepAliveTime 的时间单位,常用的有,TimeOut.MILLISECONDS(毫秒)、TimeOut.SECONDS(秒)。

  • workQueue

任务队列,execute 方法提交的 Runnable 对象会储存在这里。

  • threadFactory

线程工厂,为线程池提供创建新线程的功能。它是一个接口。

大致执行规则

  1. 当线程池中线程数小于核心线程数,启动核心线程执行该任务
  2. 当线程池中线程数大于等于核心线程数,将任务加入任务队列
  3. 当任务队列已满,并且未达到线程池最大值,启动非核心线程来执行该任务
  4. 当任务数大于最大线程数时,拒接执行任务,会调用 RejectedExecutionHandler 的 rejectedExecution 方法通知调用者

线程池的分类

  • FixedThreadPool

一种数量恒定的线程池,只有核心线程,且核心线程没有超时机制。这意味着,除非线程池被关闭,否则这些线程将一直存在。并且它的任务队列大小没有限制。该线程可以通过 newFixedThreadPool 方法来创建,方法如下

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue());
}
  • CachedThreadPool

一种数量不定的线程池,只有非核心线程,基本上无数量限制。这些线程的超时时间设置为 60 秒。它有一个特殊的任务队列 SynchronousQueue,可以被理解为一个无法储存元素的队列。所以,当有新的任务加入且无闲置线程时,会立刻启动一个线程执行该任务。该线程池适用于执行大量耗时少的任务。它使用 newCachedThreadPool 创建,方法如下

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue());
}
  • ScheduledThreadPool

它的核心线程数是固定的,而非核心线程数没有限制,但只要非核心线程闲置,就马上会被回收。它适用于执行定时和有固定周期的任务。使用 newScheduledThreadPool 创建,方法如下

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}
  • SingleThreadPool

该线程池只有一个线程,即核心线程,它确保所有的任务都在一个线程中按顺序执行。当所有的任务都添加到该线程时,可以不必考虑同步问题。它使用 newSingleThreadExecutor 方法创建,如下

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue()));
}

你可能感兴趣的:(Android 线程相关 | 艺术探索笔记)