Android进阶学习(六)Android的线程和线程池
- Android的线程和线程池
- 1 主线程和子线程
- 2 Android中的线程形态
- 2.1 AsyncTask
- 2.2 AsyncTask的工作原理
- 2.3 HandlerThread
- 2.4 IntentService
- 3 Android中的线程池
- 3.1 ThreadPoolExecutor
- 3.2 线程池的分类
- 3.2.1 FixedThreadPool
- 3.2.2 CachedThreadPool
- 3.2.3 ScheduledThreadPool
- 3.2.4 SingleThreadExecutor
文本是阅读《Android开发艺术探索》的学习笔记记录,详细内容可以自行阅读书本。
Android的线程和线程池
线程在Android中是一个很重要的概念,从用途来说,线程分为主线程和子线程。主线程主要处理和界面相关的事情,而子线程则往往用于执行耗时操作。
除了Thread本身以外,在Android中可以扮演线程角色的还有很多,比如AsyncTask和IntentService,同时HandlerThread也是一种特殊的线程。
Android线程 | 底层实现 | 特性和使用场景 |
AsyncTask | 封装了线程池和Handler | 方便开发者在子线程更新UI |
IntentService | 底层使用线程 | 内部采用HandlerThread来执行任务,执行完成后IntentService自动退出。但是IntentService是一种服务,不容易被系统杀死。 |
HandlerThread | 底层使用线程 | 具有消息循环的线程,在它内部可以使用Handler |
线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源。如果需要频繁的创建和销毁线程,正确做法就是使用线程池。Android中的线程池来源于Java,主要是通过Executor来派生特定类型的线程池。
1 主线程和子线程
Android中的线程除了传统的Thread以外,还包含AsyncTask、HandlerThread以及IntentService。
2 Android中的线程形态
2.1 AsyncTask
AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和handler,通过AsyncTask可以更加方便执行后台任务以及在主线程访问UI,但AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时任务,建议使用线程池。
AsyncTask是一个抽象的泛型类,它提供了Params、Progress和Result三个泛型参数。
参数 | 含义 |
Params | 表示参数类型 |
Progress | 表示后台任务的执行进度的类型 |
Result | 表示后台任务的返回结果的类型 |
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使用过程中,需要注意几点:
1)AsyncTask对象必须在主线程中创建。
2)execute方法必须在UI线程调用。
3)不要在程序中直接调用onPreExecute、doInBackground、onProgressUpdate和onPostExecute方法。
4)一个AsyncTask对象只能执行一次,即调用一次execute方法,否则会报异常。
2.2 AsyncTask的工作原理
分析AsyncTask的工作原理,我们先从它的execute方法开始分析,execute方法又调用executeOnExecutor方法,它们的实现如下:
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;
}
从上面代码中,sDefaultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行,稍后分析。在executeOnExecutor方法中,会对状态进行判断,如果是正在运行或者已经完成则抛出异常。onPreExecute方法会最先执行,然后线程池开始执行。接下来分析线程池的执行过程:
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的实现分析AsyncTask的排队执行过程。首先系统会把AsyncTask的Params参数封装为FutureTask对象,FutureTask是个并发类,在这里它充当了Runnable的作用。
接着SerialExecutor的execute方法首先会把FutureTask对象插入任务队列mTasks中,如果此时没有正在活动的AsyncTask,那么会调用
SerialExecutor的scheduleNext方法来执行下一个AsyncTask任务。同时当一个AsyncTask任务执行完成后,AsyncTask会继续执行其他任务直到所有任务都被执行完成。可以看出,默认情况下,AsyncTask是串行执行的。
AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler)。其中SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正地执行任务。InternalHandler用于将执行环境从线程池切换到主线程。
用于FutureTask的run会调用mWorker的call方法,因此mWorker的call方法最终会在线程池中执行。在AsyncTask的构造方法中,声明mWorker的call方法。
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;
}
};
从上述代码可知,首先将mTaskInvoked设为true,表示当前任务已经被调用过了。然后执行AsyncTask的doInBackground方法,接着将其返回值传递给postResult方法。postResult的实现如下:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
从上述代码可知,postResult方法会通过Handler发送一个MESSAGE_POST_RESULT的消息,从代码中可以看出这个getHandler方法返回的就是InternalHandler对象,这个InternalHandler定义如下:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
从上述代码可知,InternalHandler是一个静态Handler对象,为了能够切换到主线程,就需要InternalHandler对象在主线程中创建。用于静态成员会在加载类的时候进行初始化,因此就要求AsyncTask必须在主线程创建。再来看看finish方法定义:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
从上述代码可知,如果AsyncTask取消就调用onCancelled,否则就调用onPostExecute并把doInBackground的返回值传递过来。
2.3 HandlerThread
HandlerThread继承了Thread,它是一种可以使用Handler和Thread。它在run方法中通过 Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际使用中就允许在HandlerThread中创建Handler了。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
从HandlerThread的实现来看,它和普通Thread有着显著不同之处。普通Thread主要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。它在Android使用的具体场景是IntentService。
由于HandlerThread的run是无限循环,因此当明确不需要再使用HandlerThread时,可以通过它的quit或者quitSafely来终止线程的执行。
2.4 IntentService
IntentService是一个特殊的Service,它继承Service并且是一个抽象类,因此必须创建它的子类才可以使用。IntentService可用于执行后台耗时任务,当任务结束后它会自动停止。因为IntentService是服务的原因,优先级比起单纯线程高很多,所有比较适合执行一些高优先级的后台任务。
再实现上,IntentService封装了HandlerThread和Handler,可以从oncreate方法看出,如下:
@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);
}
从上述代码可以看出,当IntentService第一次被启动时,它的onCreate方法会被调用,会创建一个HandlerThread,然后使用它的Looper来构造一个Handler对象mServiceHandler。这样通过mServiceHandler发送的消息最终会在HandlerThread中执行。
每次启动IntentService,它的onStartCommand方法就会调用一次,在onStartCommand方法中处理每个后台任务的Intent。代码如下:
@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);
}
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);
}
}
可以看出,IntentService仅仅是通过mServiceHandler发送了一个消息,这个消息会在HandlerThread中处理。
然后消息会被mServiceHandler接收,会将Intent对象传给onHandleIntent方法来处理。通过解析Intent对象就可以区分具体的后台任务。当onHandleIntent方法执行完毕后,IntentService会通过stopSelf(int startId)方法来停止服务。不使用stopSelf()方法是因为,立即停止可能会有未处理完成的其他消息。
qbIntentService的onHandleIntent是一个抽象方法,需要我们在子类中实现。如果目前只有一个存活的后台任务,那么onHandleIntent执行完毕后就会停止服务。
如果目前存在多个后台任务,由于每次执行一个后台任务就必须启动一次IntentService,而IntentService内部通过消息方式向HandlerThread请求执行任务,Handler中的Looper是顺序处理消息的。这意味着IntentService也是顺序执行后台任务的,后台任务会按照外界发起的顺序排队执行。全部执行完毕后,服务才停止。
3 Android中的线程池
线程池的三个好处:
1)重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销
2)能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。。
3)能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
Android的线程池的概念来源于Java中的Executor。Executor是一个接口,真正线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池。从线程池的功能特性来说,Android的线程池主要分为4类。
3.1 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 | 线程池的核心线程数。默认情况下,核心线程会一直在线程池中一直存活,即使处于闲置状态。除非将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置核心线程会在超时后终止。 |
maximumPoolSize | 线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续新任务会被阻塞 |
keepAliveTime | 非核心线程闲置时的超时时长,超过这个时间,非核心线程会被回收。 |
unit | 用于指定keepAliveTime参数的时间单位,常用的有TimeUnit.MILLISECONDS、TimeUnit.SECONDS和TimeUnit.MINUTES |
workQueue | 线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个对象参数中 |
threadFactory | 线程工厂,为线程池提供创建新线程的功能。threadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r) |
RejectedExecutionHandler | 当线程池无法执行新的任务时,ThreadPoolExecutor会调用handler的rejectExecution方法 |
ThreadPoolExecutor执行任务大致遵循如下规则:
1)如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
2)如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
3)如果在步骤2中无法再插入任务,这个时候如果未达到线程池规定的最大值,那么就会立刻启动一个非核心线程来执行任务。
4)如果在步骤3也无法再插入任务,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知开发者。
3.2 线程池的分类
Android常见的4类具有不同功能特性的线程池,它们都是直接或者间接地通过配置ThreadPoolExecutor来实现自己的功能特性。
3.2.1 FixedThreadPool
通过Executors的newFixedThreadPool方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态时,它们不会被回收,除非线程池被关闭。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.2.2 CachedThreadPool
通过Executors的newCachedThreadPool方法来创建。它是一种线程数量不定的线程池。它只有非核心线程,并且其最大线程数量为Integer.MAX_VALUE。空闲非核心线程的超时时间为60s。这类CachedThreadPool比较适合执行大量的耗时较少的任务。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3.2.3 ScheduledThreadPool
通过Executors的ScheduledThreadPoolExecutor方法来创建。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且非核心线程闲置时会被立即回收。这类ScheduledThreadPoolExecutor线程池主要用于执行定时任务和具有固定周期的重复任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
3.2.4 SingleThreadExecutor
通过Executors的newSingleThreadExecutor方法来创建。这类newSingleThreadExecutor线程池内部只有一个核心线程,它确保所有任务都在同一个线程中按顺序执行,不需要处理线程同步问题。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
举例了经典用法:
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
fixedThreadPool.execute(runnable);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(runnable);
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
scheduledExecutorService.schedule(runnable, 2, TimeUnit.SECONDS);
scheduledExecutorService.scheduleAtFixedRate(runnable, 10, 2, TimeUnit.SECONDS);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(runnable);