一、 AsyncTask源码分析:
AsyncTask是Android中用来执行异步任务的类,底层封装了线程池和Handler。虽然现在很少使用它,它的缺点也是很明显的,比如会导致严重的内存泄漏等。尽管如此,抱着学习的态度,它的源码还是值得分析的。首先还是略微就AsyncTask的基本使用来进行一些说明:
- 它是一个抽象类,如果我们需要使用它需要创建一个类来继承AsyncTask。
- AsyncTask有一个抽象方法,子类必须实现:
- doInBackground(Params... params):在子线程中执行(实际上是在AsyncTask底层维护的线程池中执行),参数params表示传入的参数。在此方法中可以通过publishProgress方法来更新任务的进度,publishProgress方法会导致onProgressUpdate方法的执行,另外此方法还有一个任务就是需要返回结果给onPostExecute()。
- 还有几个重要方法:
onPreExecute():在主线程中执行,一般用于做一些准备工作
onPostExecute():在主线程中执行,当异步任务执行结束之后此方法得到调用。
onProgressUpdate():运行在主线程中,用来更新任务的进度;
publishProgress():在doInBackground中调用,通知进度更新,从而导致onProgressUpdate()方法得到执行;
execute(Params... params):用来开始异步任务;
onCanceled():运行在主线程中,当异步任务被取消时,此方法会被调用,而onPostExecute方法不会被回调。
- AsyncTask有三个泛型参数:<Params, Progress, Result>,分别表示的意思是:
Params:表示传入参数的类型,也就是execute、doInBackgroud方法的参数类型,一般为String类型,传入所需的url。
Progress:表示进度的类型,一般是Integer。
Result:表示返回结果的类型,是doInBackground方法的返回类型,也是onPostExecute方法的参数类型。
-
下面给出一般使用的示例代码:
public class DownloadTask extends AsyncTask<String, Integer, Integer>{ protected void onPreExecute(){ showDialog(); } protected Integer doInBackground(String... params){ //联网下载,在子线程中 } protected void onPostExecute(Integer status){ mDialog.dismiss(); } protected void onProgressUpdate(Integer... progress){ mProgressBar.setProgress(progress[0]); } }
然后调用AsyncTask的execute(“http://www.baidu.com”)方法。这样就可以实现具体异步功能了。下面才是重点,才更有价值,进行源码分析(版本是25)。
1. 直接从execute()方法入手:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
直接调executeOnExecutor方法
2. 看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;
}
首先根据状态来进行判断,如果已经在运行或者已经结束则直接抛出异常,不再往下进行。然后把mStatus状态置为运行状态。接着,我们会看到一个熟悉的方法->onPreExecute(),这个时候程序还是运行在主线程(前提是execute在主线程中调用的,并且官方文档也指明了这一点),正好印证了前面的结论:此方法是在异步任务开始前得到执行的,并且运行在主线程中。
3. 再往下看,重点是exec.excute(mFuture)这行代码,前一行代码暂且不看。先看exec是何方神圣:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
注意这个sDefaultExecutor是static的
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new 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)的对象
- 将其放入集合mTask中
- 调用scheduleNext()方法。
第一次进来,mActive为null,则会调用scheduleNext方法。而scheduleNext方法呢?就是从集合中取出Runnable对象传入线程池执行。
4. 看线程池
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
对线程池不理解也没关系,可以将其看做是new Thread(new Runnable{})形式即可。换句话说,也就是从现在开始,程序的执行已经放在了子线程中。那到底在子线程中干了什么呢?我们来看前面创建的匿名内部类的run方法:直接调用传入的Runnable的run方法,执行完后接着调用scheduleNext方法。这样,我们就可以不断从mTask集合中不断取出异步任务,然后去执行。实现了一种类似递归的机制(和Handler在handleMessage方法中再次发消息的机制类似)。实际上,我们从这里也可看出:如果我们有多个异步任务,那么他们实际上是串行去处理的,并非并行(因为sDefaultExecutor是静态的)。 现在,我们就来看看传给SerialExecutor的execute方法的Runnable的run方法中到底干了什么(因为前面提到的匿名Runnable对象并没干实质性的工作,它的作用仅仅是负责当一个异步任务执行完成后,然后再去取其它任务让其得以执行。仔细想想,这个地方用到了代理模式)。
mWorker.mParams = params;
exec.execute(mFuture);
private final FutureTask<Result> mFuture;
那么mFuture在哪里初始化的呢?我们来看构造器。
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
* */
public AsyncTask() {
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;
}
};
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);
}
}
};
}
这个FutureTask实现了RunnableFuture接口,而RunnableFuture又是继承自Runnable接口。也就是说实际上我们就只需要看FutureTask的run方法是如何实现的就行了。
public void run() {
if (state != NEW ||!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
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);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
重点看两行:
Callable c = callable;
result = c.call();
调了callable的call方法,那么这个callable引用到底指向谁?我们来看FutureTask的构造器:
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
从构造器中传入的。
5. 我们再回到AsyncTask的构造器中:
mFuture = new FutureTask<Result>(mWorker)
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;
}
};
那现在我们就明白了,实质性工作是在这里的call方法中进行的:
- 调用了熟悉的doInBackground方法
-
调用了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就是在构造Message,然后通过Handler发送出去。分析也就到了Handler的实现上,阅读getHandler()的源码:
private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return 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 result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } }
根据消息的类型,执行不同的操作。在这里我们看到了熟悉的onProgressUpdate()方法得到了调用,还有几个熟悉的方法不是那么明显,就是这里的finish方法,即AysncTask的finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在这里我们看到了或者onPostExecute()或者onCancelled()得到了调用。
还有一个问题,咱们可以去思考一下,这个Handler最终是切换到了主线程去运行了吗?是,何以见得?当然是此处Handler的构造器
public InternalHandler() {
super(Looper.getMainLooper());
}
InternalHandler的Looper是主线程的Looper。如果还不理解的话,就需要看Handler源码分析了。Handler源码分析。
6. 适当总结整个工作流程:
首先,AsyncTask在其构造器中初始化了两个域,一个是WorkRunnable的子类,另一个是FutureTask的子类。其次,我们的分析要从execute开始: AsyncTask.execute()-> AsycnTask.executeOnExecutor(),然后根据当前AsyncTask的状态来区分,不是PENDING状态的都抛异常。然后改状态为运行,记录传入execute的参数到mWorker,同时回调onPreExecute()方法,最后启动线程池执行任务,这个线程池是串行执行任务的。在线程池执行任务过程中,会调用前面在构造器中初始化的FutureTask的run方法,在run方法中又会WorkRunnable的call方法,call方法里会回调doInBackground,最终执行FutureTask的done方法,在此方法中会通过Handler转换到主线程然后回调onPostExecute()方法。
二、线程池(以下部分摘自Android开发艺术探索)
1. 使用线程池的好处:
- 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销
- 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
- 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能
2. ThreadPoolExecutor的相关参数说明:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
- corePoolSize:线程池的核心数,默认情况下,核心线程会在线程中一直存活,即使它们处于闲置状态。如果allowCoreThreadTimeOut的属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime所指定,当等待时间超过keepAliveTime所指定时长时,核心线程就会被终止
- maximunPoolSize:线程池所能容纳的最大线程数,当活动线程达到这个数后,后续的新任务将会被阻塞
- keepAliveTime:非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。
- unit:用于指定keepAliveTime参数的时间单位
- workQueue:线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中
- threadFactory:线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r)
3. ThreadPoolExecutor执行任务时大致遵循如下规则:
- 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
- 如果线程池中的线程数量已经达到或者超过核心线程数,那么任务会被插入到任务队列中排队等待执行
- 如果步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定最大值,那么就会立刻启动一个非核心线程来执行任务。
- 如果在步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectExecutionHandler的rejectedExecution方法来通知调用者。
4.在AysncTask中线程池的配置:
a) 核心线程数 = CPU核心数 + 1
b) 线程池最大线程数 = CPU核心数 * 2 + 1
c) 核心线程无超时机制,非核心线程在闲置的超时时间为1秒
d) 任务队列的容量为128
5. 线程池的分类
-
FixedThreadPool:线程数量固定的线程池。当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。本质:只有核心线程,并且核心线程没有超时机制,任务队列也没有大小限制
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
-
CachedThreadPool:线程数量不定,只有非核心线程,最大线程数为Integer.MAX_VALUE。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理任务,否则就会利用空闲的线程来处理新任务。空闲线程有超时机制,时长为60秒钟,超过60秒钟闲置线程就会被回收。任务队列为空集合。适合执行大量的耗时比较少的任务。OkHttp的Dispatcher中使用的就是类似线程池(2017.12.26日添加)。
- ScheduledThreadPool:核心线程数固定,非核心线程数没有限制,非核心线程空闲时立即被回收
- SingleThreadPool:只有一个核心线程。
6. 总结口诀,帮助记忆四种特殊线程池:
(姗姗KFC) ------即ssfc,每句首字母,我有个女同学叫姗姗
Single专一爱一人
S原配妾即丢 -----原配:比喻成核心线程,妾比喻非核心线程
F妻定不要妾,来者不拒 ---备胎是指任务队列
C与妾情60秒,无备胎