AsyncTask源码深入分析和巧记线程池
一、 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:表示传入参数的类型,也就是execute、doInBackgroud方法的参数类型,一般为String类型,传入所需的url。
Progress:表示进度的类型,一般是Integer。
Result:表示返回结果的类型,是doInBackground方法的返回类型,也是onPostExecute方法的参数类型。
-
下面给出一般使用的示例代码:
public class DownloadTask extends AsyncTask
{ 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 execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);}
直接调executeOnExecutor方法
2. 看executeOnExecutor方法的具体实现:
public final AsyncTask 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是何方神圣:
@MainThreadpublic final AsyncTask 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 mTasks = new ArrayDeque(); 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 mFuture;
那么mFuture在哪里初始化的呢?我们来看构造器。
/*** Creates a new asynchronous task. This constructor must be invoked on the UI thread.* */public AsyncTask() { mWorker = new WorkerRunnable() { 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(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 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 callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable}
从构造器中传入的。
5. 我们再回到AsyncTask的构造器中:
mFuture = new FutureTask(mWorker) mWorker = new WorkerRunnable() { 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
(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 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核心数 + 1b) 线程池最大线程数 = CPU核心数 * 2 + 1c) 核心线程无超时机制,非核心线程在闲置的超时时间为1秒d) 任务队列的容量为128
5. 线程池的分类
-
FixedThreadPool:线程数量固定的线程池。当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。本质:只有核心线程,并且核心线程没有超时机制,任务队列也没有大小限制
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
());} -
CachedThreadPool:线程数量不定,只有非核心线程,最大线程数为Integer.MAX_VALUE。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理任务,否则就会利用空闲的线程来处理新任务。空闲线程有超时机制,时长为60秒钟,超过60秒钟闲置线程就会被回收。任务队列为空集合。适合执行大量的耗时比较少的任务。OkHttp的Dispatcher中使用的就是类似线程池(2017.12.26日添加)。
- ScheduledThreadPool:核心线程数固定,非核心线程数没有限制,非核心线程空闲时立即被回收
- SingleThreadPool:只有一个核心线程。
6. 总结口诀,帮助记忆四种特殊线程池:
(姗姗KFC) ------即ssfc,每句首字母,我有个女同学叫姗姗Single专一爱一人S原配妾即丢 -----原配:比喻成核心线程,妾比喻非核心线程F妻定不要妾,来者不拒 ---备胎是指任务队列C与妾情60秒,无备胎
更多相关文章
- 深入理解Binder通信原理及面试问题
- Android(java方法)上实现mp4的分割和拼接 (一)
- AsyncTask使用以及源码分析
- Android源代码编译和运行常见错误解决方案
- 这种方式教你简单的在Flutter中分离View与Model的方法
- Android(安卓)线程通信,初见
- Android仿人人客户端(v5.7.1)——网络模块处理的架构
- Android之完美退出方法
- Android梳理 Activity