线程分为主线程和子线程,主线程主要处理和界面有关的事情,而子线程往往用于执行耗时操作;
除了Thread,扮演线程角色的还有很多,如AsyncTask,IntentService,HandlerService,
AsyncTask底层用到的是线程池,而IntentService和HandlerService底层直接使用了线程。
AsyncTask封装了线程池和Handler,它主要是方便在主线程更新UI;
HandlerThread是一种具有消息循环的线程,它的内部可以使用Handler;
IntentService是一个服务,系统对其进行了封装,使其可以更方便的执行后台任务;IntentService内部采用了HandlerThread来执行任务,当任务执行完毕后,IntentService会自动退出。
IntentService更像是一个后台线程,但是它是一种服务,它不容易被系统杀死从而保证任务的执行;而如果是一个后台线程,由于这个时候进程中没有活动的四大组件,那么这个进程的优先级就会非常低,会很容易被系统杀死,这就是使用IntentService的优点。
AsyncTask:
好处:简化子线程中访问UI的过程;
AsyncTask是一个轻量级的异步任务类,它可以在线程中执行后台任务,然后把执行的进度和最终结果传递给主线程,并且在主线程上更新UI。
AsyncTask并不适合执行特别耗时的任务,对于特别耗时的任务来说,建议使用线程池。
AsyncTask是一个泛型类,它提供了Params,Progress和Result三个泛型参数,Params表示参数的类型,Progress表示后台任务的执行进度的类型,Result是后台任务的返回结果类型;如果AsyncTask确实不需要传递具体的参数,那么这三个泛型参数可以用Void来代替;
具体声明如下:
public abstract class AsyncTask<Params,Progress,Result>。
它有4个核心方法:
onPreExecute(),在主线程执行,在异步任务执行之前会被调用,一般可以用于做些准备工作;
doInBackground(Params... params),在线程池中执行,此方法用于执行后台任务,params表示输入的参数。此方法中可调用publishProgress方法来更新任务的进度,publishProgress会调用onProgressUpdate方法,另外,此方法需要返回结算结果给OnPostExcute方法;
onProgressUpdate(Progress... values),在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用;
onPostExecute(Result result),在主线程执行,在异步任务执行之后调用,其中result参数是后台任务的返回值,即doInBackground的返回值;
onCanceled(),在主线程中执行,当异步任务取消时,该方法会被调用,这个时候,onPostExecute不会被执行;
注意以下几点:
1.AsyncTask的类必须要在主线程中加载,即第一次访问AsyncTask必须发生在主线程中;
2.AsyncTask的对象必须要在主线程中去创建;
3.execute方法必须要在UI线程中执行;
其中1.2.3只在android2.3以后不作要求,后面会解释;
4.一个AsyncTask对象只能执行一次,即只能调用一次execute,否则会报异常;
5.通过AsyncTask的executeOnExecutor方法来并行的执行任务;
分析流程:
1.来看execute方法,他会调用到executeOnExecutor方法:
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;
}
其中,extc是是一个串行的线程池,一个进程中的所有的AsyncTask全部在这个串行的线程池中排队,如下:
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);
}
}
}
来看executeOnExecutor中的exec.execute(mFuture)这个方法,mFuture实在AsyncTask的构造函数中被定义的
来看AsyncTask的构造方法:
public AsyncTask() {
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);
}
};
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);
}
}
};
}
系统将AsyncTask的Params参数封装为FutureTask对象,FutureTask在这里充当了Runnable的作用;
exec.execute(mFuture):将Future对象插入到任务队列mTasks,这里的SerialExecutor线程池是被static修饰的,也就是该SerialExecutor被所有的AsyncTask共享的,所以,每个AsyncTask的Future对象都被插入到共享任务队列mTasks中,再进行判断此时是否有正在活动的AsyncTask任务,如果没有,即mActive == null,也就是任务队列中无内容,此时便会调用scheduleNext方法,立即执行该AsyncTask的请求;如果有此时有活动的AsyncTask任务正在执行的话,便等待上个AsyncTask执行完;当上个AsyncTask执行完,会调用scheduleNext方法执行下一个任务直到所有的任务被执行为止;
可以看出,在默认情况下,AsyncTask是串行执行的;
2.scheduleNext()方法:
THREAD_POOL_EXECUTOR.execute(mActive);
THREAD_POOL_EXECUTOR是真正的执行任务的线程池,如下
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
这个线程池被static final修饰,即该线程池是被所有的AsyncTask子类共享的,所有的线程任务都在该线程池中被执行;
3.mFutureTask的run方法,会调用mWorker的call()方法,在该call方法里,先后调用了doInBackground和postResult方法;
4.postResult方法会使用到一个Handler,该Handler就是通过主线程的Looper创建的;
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
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
在android2.3以前,因为我们要将执行环境切换到主线程,这就要求该Handler对象必须要在主线程中创建,InternalHandler 是被static修饰的,静态成员会在加载类的时候被初始化,所以要求AsyncTask的类第一次使用时必须要在主线程中加载;但是在android2.3以后,InternalHandler使用的是主线程的looper,也就是说,AsyncTask可以在任意线程中使用;
从以上分析我们可以看出,AsyncTask是串行执行,即前一个完成之后,才能进行下一个,也就变相的说明了,在AsyncTask中,不能执行太耗时的任务;
为了让AsyncTask可以并发的执行,我们需要替换线程池,将串行的线程池exec替换成THREAD_POOL_EXECUTOR,调用方法如下:
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params);
当然我们也可以使用其他线程池来代替THREAD_POOL_EXECUTOR;
替换了线程池之后,便可以并发执行的需求;
总结:
AsyncTask中有两个线程池(SerialExecutor 和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler);
其中SerialExecutor用于任务的排队,而THREAD_POOL_EXECUTOR用于真正的执行任务,InternalHandler用于将执行环境切换从线程池到主线程去执行;