在 Android 中,除了 Thread 以外,还有 AsyncTask、HandlerThread 和 IntentService 充当线程的作用,它们各自有不同的特点和适用场景。
AsyncTask 封装了线程池和 Handler,它能方便使用者在子线程中更新 UI。HandlerThread 是使用 Handler 的线程。IntentService 是一个服务,它比一般的后台线程优先级更高,不容易被系统杀死,所以可以更方便的执行后台线程。
AsyncTask
AsyncTask 是一个轻量级的异步任务类,它不适合进行特别耗时的任务。
用法
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask 是一个抽象的泛型类,它提供了 Params、Progress、Result 三个泛型参数。Params 表示参数的类型,Progress 表示后台任务的执行进度,Result 表示后台任务返回结果的类型。如果 AsyncTask 不需要传递参数,以上三个都可以用 Void 来替代。
AsyncTask 有四个核心方法
- onPreExecute()
- doInBackground(Params... params)
- onProgressUpdate(Progress... values)
- 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<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> 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<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 = 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<V> 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<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask<Result>(mWorker) {
...
};
}
public FutureTask(Callable<V> 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<Result>(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<Runnable> 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
线程工厂,为线程池提供创建新线程的功能。它是一个接口。
大致执行规则
- 当线程池中线程数小于核心线程数,启动核心线程执行该任务
- 当线程池中线程数大于等于核心线程数,将任务加入任务队列
- 当任务队列已满,并且未达到线程池最大值,启动非核心线程来执行该任务
- 当任务数大于最大线程数时,拒接执行任务,会调用 RejectedExecutionHandler 的 rejectedExecution 方法通知调用者
线程池的分类
- FixedThreadPool
一种数量恒定的线程池,只有核心线程,且核心线程没有超时机制。这意味着,除非线程池被关闭,否则这些线程将一直存在。并且它的任务队列大小没有限制。该线程可以通过 newFixedThreadPool 方法来创建,方法如下
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- CachedThreadPool
一种数量不定的线程池,只有非核心线程,基本上无数量限制。这些线程的超时时间设置为 60 秒。它有一个特殊的任务队列 SynchronousQueue,可以被理解为一个无法储存元素的队列。所以,当有新的任务加入且无闲置线程时,会立刻启动一个线程执行该任务。该线程池适用于执行大量耗时少的任务。它使用 newCachedThreadPool 创建,方法如下
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 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<Runnable>()));
}