Android的线程和线程池
线程是Android中的一个很重要的概念,从用途上来说:线程分为主线程和子线程,主线程主要处理和界面相关的事情,而子线程则往往用来执行耗时操作。
Android中的线程:
Thread
AsyncTask:封装了线程池和Handler,主要是方便开发者在子线程中更新UI。
IntentService:是一个服务,系统对其进行了封装使其可以更方便的执行后台任务,IntentService内部采用HandlerThread来执行任务,当任务执行完毕后,IntentService会自动退出。
HandlerThread:是一个具有消息循环的线程,在它的内部可以使用Handler。
主线程和子线程
主线程是指进程所拥有的线程,在java默认情况下一个进程只有一个线程,这个线程就是主线程。其他线程为子线程(工作线程)。
AsyncTask
AsyncTask是一种轻量级的异步任务类,他可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
AsyncTask是一个抽象的泛型类,它提供了Params、progress、Result这三个泛型参数。
Params:参数类型
progress:后台任务的执行进度
Result:表示后台任务的返回结果
public abstract class AsyncTask<Params, Progress, Result> {}
AsyncTask提供了四个核心方法:
public static class myTask extends AsyncTask<Void,Integer,String>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
@Override
protected String doInBackground(Void... params) {
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
(1)onPreExecute
在主线程中执行,在异步任务执行之前,次方法会被调用,一般可用于做一些准备工作。
(2)doInBackground(Void… params)
在线程中执行,次方法用于执行异步任务,params表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法。另外次方法需要返回结果给onPostExecute()方法。
(3)onProgressUpdate(Integer… values)
在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
(4)onPostExecute(Result result)
在主线程中执行,在异步任务结束执行之后,此方法会被调用,其中result参数是后台任务的返回值。
上面的方法,onPreExecute先执行,然后是doInBackground最后才是onPostExecute。此外AsyncTask还提供了onCancelled()方法,同样在主线程中执行,当异步任务被取消时,onCancel()方法会被调用。
使用时注意事项:
1、AsyncTask对象必须在主线程中创建。
2、execute方法必须在UI线程中调用。
3、不要在程序中直接调用onPreExecute()、onPostExecute()、doInBackground和onProcessUpdate方法。
4、一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常。
5、Android3.0以后通过executeOnExecutor方法来并行执行任务。
AsyncTask工作原理
分析AsyncTask的工作原理,首先从它的execute方法开始分析,execute方法又会调用executeOnExecutor方法。
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
/**sDefaultExecutor实际上是一个串行的线程池*/
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
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方法*/
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
然后线程池开始执行:
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);
}
}
}
以上代码我们可以看到,AsyncTask在执行的过程中,首先会把Params参数封装成为FutureTask对象,FutureTask是一个并发类,在这里它充当了Runnable的作用。
接着这个FutureTask会交给SerialExecutor的execute方法去处理,execute方法首先会把AsyncTask对象插入到任务队列mTask中,如果这个时候没有正在活动的AsyncTask任务,那么就会抵用SerialExecutor 的scheduleNext方法来执行下一个AsyncTask任务。
AsyncTask中有两个线程池(SerialExecutor和THREAD_POOP_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOP_EXECUTOR用于真正的执行任务,InternalHandler用于将执行环境从线程池切换到主线程。
在AsyncTask的构造方法中,有以下代码,由于Future的run方法会调用mWorker的call方法,因此mWorker的call方法最终会在线程池中进行。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
在上面代码中,mWorker 的call方法首先将mTaskInvoked设置为true,表示当前任务已经被调用过了,然后执行AsyncTask的doInBackground方法,接着将其返回值传递给postResult方法:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
/**通过sHndler发送一个MESSAGE_POST_RESULT的消息*/
message.sendToTarget();
return result;
}
sHandler定义如下:sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler对象必须在主线程中创建。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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
/**收到消息后调用AsyncTask的finish方法*/
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
我们再来看AsyncTask的finish方法。
private void finish(Result result) {
if (isCancelled()) {
//如果被取消,调用onCancelled方法
onCancelled(result);
} else {
//否则调用onPostExecute方法
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
这里需要注意一点:execute方法是静态方法,doInBackground方法是在构造方法中调用的。 所以execute后面调用的onpreExecute()方法是优先于doInBackground方法执行的。
HandlerThread
HandlerThread继承了Thread,它是一种可以使用Handler的Thread,它的实现很简单。就是在run方法中通过Looper.prepare()来创建爱你消息队列,并通过Looper.loop()来开启消息循环。这样在实际的应用中就允许在HandlerThread中创建Handler了。
@Override
public void run() {
mTid = Process.myTid();
/**为当前线程创建Looper*/
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
/**开启消息循环*/
Looper.loop();
mTid = -1;
}
HandlerThread一个具体的使用场景是IntentService,由于HandlerThread的run方法是一个无限循环,因此当不需要HandlerThread时,可以通过它的quit和quitSafely方法来终止线程的执行。
IntentService
IntentService是一种特殊的Service,它继承了Service 并且它是一个抽象类,因此必须实现它的子类才能使用IntentService。
IntentService可用于执行后台耗时的任务,当任务执行完成后它会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的线程要高很多。所以IntentService比较适合执行一些高优先级的后台任务,不容易被系统杀死。
实际上,IntentService封装了HandlerThread和Handler,我们来看一下它的onCreate方法:
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
}
当IntentService 被第一次启动时,onCreate方法会创建一个HandlerThread,然后会使用它的Looper来构造一个Handler对象ServiceHandler,这样通过ServiceHandler发送的消息最终都会在HandlerThread中执行。
我们来看一下IntentService 的工作过程:我们首先看一下它的onStartCommand方法。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
onStart方法中使用mServiceHandler发送了一条消息,在HandlerThread中处理。
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);
}
}
然后我们看到ServiceHandler处理消息,在onHandleIntent方法完成之后,使用stopSelf停止自身。
需要注意的是:如果目前只存在一个后台任务,那么onHandlerIntent方法执行完成这个任务后,stopSelf(msg.arg1);就会直接停止服务;如果目前存在多个后台任务,那么当onHandlerIntent方法执行完最后一个任务时,stopSelf(msg.arg1)才会直接停止服务。
Handler中的Looper是顺序处理消息的,所以IntentService 也是顺序执行后台任务的。
Android中的线程池
使用线程池的优点:
1、重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
2、能够有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
3、能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
Android中的线程池概念来自于java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。
ThreadPoolExecutor
构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize
线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态。
还可以设置ThreadPoolExecutor等待任务的超时策略:allowCoreThreadTimeOut属性设置为true
maximumPoolSize
线程池所能容纳的最大线程数,如果线程数达到这个数值后,后续的任务会被阻塞。
keepAliveTime
非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。
unit
用于指定keepAliveTime参数的事件单位,TimeUnit.MILLISECONDS(毫秒)、TimeUnit .SECONDS(秒)、TimeUnit MINUTES(分钟)等。
workQueue
线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
AsyncTask中线程池的配置情况:配置后的线程规格如下
1、核心线程数等于CPU核心数+1
2、线程池的最大线程数为CPU核心数的2倍+1
3、核心线程无超时机制,非核心线程在闲置时的超时时间为1秒
4、任务队列的容量为128
线程池的分类
1、FixedThreadPool
通过Execute的newFixedThreadPool方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收。
2、CachedThreadPool
通过Execute的newCachedThreadPool方法来创建。它是一种线程数量不定的线程池,只有非核心线程,并且最大线程数为Integer.MAX_VALUE。
3、ScheduledThreadPool
通过Execute的newScheduledThreadPool方法来创建。核心线程数量固定,非核心线程数量没有限制,而且非核心线程闲置时会被立即回收。
4、SingleThreadExecutor
通过Execute的newSingleThreadExecutor方法来创建。内部只有一个核心线程,并且确保所有的任务都在同一个线程中按顺序进行。