1,前段时间换工做的时候,关于AsyncTask源码这个点基本上大一点的公司都会问,因此今天就和你们一块儿来总结总结。原本早就想写这篇文章的,当时写《Android -- 从源码解析Handle+Looper+MessageQueue机制》的时候就是想为这篇文章作铺垫的,由于AsyncTask说里面仍是使用的handle,因此先就写了handle这一篇。记得15年末去美团面试的时候,面试官就问我既然存在handle为何google还要出AsyncTask(毕竟底层仍是用的handle+Executor),当时也是不太懂。只是简单的说了下AsyncTask是对handle的封装,可能更加优化,性能好之类的。因此今天就带你们一块儿看一下它底层究竟是怎么实现的。html
2,分析+实例java
在12年以前那时候尚未一些xtuils、volley的第三方的网络框架,也没有asyncTask的出现,那时候要请求一个网络数据,首先build request参数,而后因为请求网络是耗时操做,因此你得有个Executer或者线程,而后enqueue后,经过线程去run你的请求,获得服务器数据后,callback回调给你的上层。android
在没有框架的年代,想要作一次请求,是万分痛苦的,你须要本身管理线程切换,须要本身解析读取数据,解析数据成对象,切换回主线程,回调给上层。git
而后咱们的AsyncTask顺势而生了,它不须要咱们程序员再手动管理线程,动手写回调之类,为了防止有些同窗压根都不知道这个类,因此这里我仍是带着你们从一些概念到实例,再到源码。程序员
public abstract class AsyncTask<Params, Progress, Result> { }
当咱们使用asynctask的时候通常都会建立一个类继承自它,且须要肯定它的三个泛型,这里的三种泛型类型分别表明“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并非全部类型都被使用,若是没有被使用,能够用Java.lang.Void类型代替。github
在通常的get请求状况下咱们第一个参数是传递的咱们请求的地址,因此这里咱们会传递一个String,而第二个参数是咱们的后台执行的进度,若是你的需求须要实时的向用户展现请求的进度的话这里就能够填写Integer类型,否写能够写上Void,第三个参数是咱们请求的结果,通常是byte[],String等,能够根据具体的需求。面试
再让咱们看看它里面的几个重要的方法:服务器
① onPreExecute():通常用来在执行后台任务前对UI作一些标记,例如dialog的show。 ② doInBackground(Params... params) :用于执行较为耗时的操做,此方法将接收输入参数和返回计算结果。在执行过程当中能够调用publishProgress(Progress... values)来更新进度信息。 ③ publishProgress(Progress... values):用来更新进度 ④ onProgressUpdate(Progress... values):在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。 ⑤.onPostExecute(Result result):当后台操做结束时,此方法将会被调用,计算结果将作为参数传递到此方法中,将结果回调显示到UI组件上。
这里须要注意几个点:上面这五个方法都不是手动调用的,当你调用ascyntask.execute()方法以后上面的方法会自动调用、doInBackground是运行在子线程,不能进行ui操做。好了,大体的知识点都了解了,咱们开始写一个栗子。网络
先看一下效果:并发
让咱们直接来看一下自定义的MyAsyncTask的代码:
MyAsyncTask.java
public class MyAsyncTask extends AsyncTask<String ,Integer,byte[]> { /** * 被调用后当即执行,通常用来在执行后台任务前对UI作一些标记 */ @Override protected void onPreExecute() { super.onPreExecute(); Log.i("wangjitao","onPreExecute"+Thread.currentThread().getName()); } /** * 在onPreExecute()完成后当即执行,用于执行较为费时的操做,此方法将接收输入参数和返回计算结果。在执行过程当中能够调用publishProgress(Progress... values)来更新进度信息。 * @param params * @return */ @Override protected byte[] doInBackground(String... params) { Log.i("wangjitao","doInBackground"+Thread.currentThread().getName()); //请求网络数据 InputStream inputStream = null; HttpURLConnection httpURLConnection = null; try { URL url = new URL(params[0]); if (url != null) { httpURLConnection = (HttpURLConnection) url.openConnection(); // 设置链接网络的超时时间 httpURLConnection.setRequestMethod("GET");//GET和POST必须全大写 httpURLConnection.setConnectTimeout(10000);//链接的超时时间 httpURLConnection.setReadTimeout(5000);//读数据的超时时间 // 表示设置本次http请求使用GET方式请求 httpURLConnection.setRequestMethod("GET"); int responseCode = httpURLConnection.getResponseCode(); if (responseCode == 200) { // 从服务器得到一个输入流 InputStream is = httpURLConnection.getInputStream(); long total = httpURLConnection.getContentLength(); int count = 0; ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len=0; while((len=is.read(buffer))!=-1){ baos.write(buffer, 0, len); count += len; publishProgress((int) ((count / (float) total) * 100)); //为了演示进度,休眠500毫秒 Thread.sleep(500); } is.close(); byte[] datas = baos.toByteArray(); baos.close(); return datas; } } } catch (Exception e) { } return null; } /** * 当前进度的更新 * @param values */ @Override protected void onProgressUpdate(Integer... values) { Log.i("wangjitao","onProgressUpdate"+Thread.currentThread().getName()); mDialog.setProgress(values[0]); } /** * 当后台操做结束时,此方法将会被调用,计算结果将作为参数传递到此方法中,直接将结果显示到UI组件上。 * @param bytes */ @Override protected void onPostExecute(byte[] bytes) { Log.i("wangjitao","onPostExecute"+Thread.currentThread().getName()); mDialog.dismiss(); mImageView.setImageBitmap(BitmapFactory.decodeByteArray(bytes, 0, bytes.length)); } }
这里在点击button以后我调用了new MyAsyncTask().execute(url);,而后再MyAsyncTask的每一个方法里面添加上了当前线程的log,想看一下doInBackground是否是像咱们网上说的运行在子线程,这里注意一下,咱们请求的是网络数据,记得在清单文件加上网络权限,打印结果以下:
08-16 05:36:24.978 19453-19453/com.ysten.anysctasksource I/wangjitao: onPreExecute:main 08-16 05:36:24.979 19453-23587/com.ysten.anysctasksource I/wangjitao: doInBackground:AsyncTask #1 08-16 05:36:28.573 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:29.074 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:29.574 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:30.075 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:30.575 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:31.086 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:31.588 19453-19453/com.ysten.anysctasksource I/wangjitao: onPostExecute:main
能够看到doInBackground的确运行在子线程中,过一下咱们从源码里面来验证一下。
3,源码分析
咱们知道启动咱们整个任务的就是咱们new MyAsyncTask().execute(url);方法,因此咱们首先来看一下execute()方法
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { 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(); mWorker.mParams = params; exec.execute(mFuture); return this; }
第3行:咱们知道execute方法是调用本类的executeOnExecutor。
第10-20行:用来判断当前AsyncTask的状态,一共存在三种状态 :PENDING(就绪)、RUNNING(运行)、FINDISH(完成),保证每次都是第一次运行此异步任务,否则就抛异常
第22行:将当前的运行状态切换至RUNNING状态
第24行:执行了咱们的onPreExecute() ,注意一下,这时候咱们的第一个准备操做的方法被执行了(因此通常咱们在这个方法作一些ui的准备操做)
第26行:将咱们的params复制成员变量mWorker对象的mParams上(有同窗说这里我还不知道mWorker,先不要慌,继续往下看)
第27行:exec.execute(mFuture);这行代码咱们更加一脸懵逼,mFuture、exec分别表明什么对象呢?exec.execute()是用来干什么的呢?
ok,看完 上述代码咱们必定对mWorker、mFuture、exec这些成员变量有所疑惑吧,没事咱们来一点点的来看。
首先来看一下咱们的mWorker到底是什么,源码以下:
private final WorkerRunnable<Params, Result> mWorker; private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; } public interface Callable<V> { V call() throws Exception; } 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; } };
第4-10行:能够看到咱们的mWorker是Callable的子类,包含一个call方法和一个变量mParams。
这里要补充一下,mWorker是在构造函数中初始化的,由于是一个抽象类,在这里new了一个实现类,实现了call方法
第14-27行:首先将mTaskInvoked的value设置成true(这里要留心一下这个操做,后面还有地方对mTaskInvoked进行判断的),而后Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);设置子线程优先执行,而后就执行到了doInBackground(),看到没,这里就到了咱们的第二个方法,这里还有一个疑问,咱们上面知道doInBackground是执行在子线程中的,那怎么执行在子线程中呢(咱们带着这个问题),先继续往下看。
当运行到finally代码块中调用的是 postResult(result);咱们接下来看看postResult()的具体代码:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } @SuppressWarnings({"RawUseOfParameterizedType"}) private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }
第3-5行:很熟悉没?了解咱们的异步消息handle的,都知道这是干什么的而 AsyncTaskResult类就是把当前从网络下请求的结果数据result保存到data中
看到这,我相信你们确定会想到,在某处确定存在一个handler,且重写了handleMessage方法等待消息的传入
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; } } }
果真,在handleMessage中咱们的MESSAGE_POST_RESULT以后时调用的自己的finish()方法(这里也请你们留心一下MESSAGE_POST_PROGRESS这个消息,后面也会用到),那么让咱们再看看finish里面的具体代码吧
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
首先判断当前的asynctask是否被取消,若果没取消则执行onPostExecute(result),这时候数据已经回调了,到这里咱们就差很少执行完了几个重要的方法了,而后再将asynctask的执行状态切换到FINISH状态。
到这里咱们的mWorker的工做流程所有了解了,继续往下看到咱们mFuture这个变量
private final FutureTask<Result> mFuture; 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); } } };
mFuture这个变量也是初始化在咱们的构造函数里面,首先咱们不去看FutureTask这个类中的具体代码包含的方法和变量(因为代码有点多,后面的话单独的给你们分析一下),咱们先继续往下看,mFuture的初始化是将将mWorker做为参数,并重写其done()方法。
done方法中具体的时调用postResultIfNotInvoked()方法,而get()方法里面是获取的咱们的Result泛型,拿到的是咱们mWorker.call的返回值,看一下具体的代码
private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { postResult(result); } }
若是mTaskInvoked不为true,则执行postResult;可是在mWorker初始化时就已经将mTaskInvoked为true(我在上面call方法里面提醒过你们的),因此通常这个postResult执行不到。
ok,到这里咱们咱们基本上都了解都mWorker、mFuture的意思了,接下来看看咱们的exec这个变量的含义吧
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 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); } } }
第1-3行:从上面的代码咱们能够知道,exec为executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor
第5-7行:sDefaultExecutor其实是一个SerialExecutor对象
第9-26行:这里咱们能够看到SerialExecutor里面内部维持一个任务队列,在execute方法中将runnable放入mTasks队尾,而后判断当前mActive是否为空,若是不为空在调用scheduleNext()方法;
第28-33行:取出任务队列中的队首任务,若是不为null则赋值给mActive对象并传入THREAD_POOL_EXECUTOR进行执行。
因此到这里咱们再来看看THREAD_POOL_EXECUTOR是一个什么鬼!
public static final Executor THREAD_POOL_EXECUTOR; ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE_SECONDS = 30;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
能够看到THREAD_POOL_EXECUTOR其实就是一个线程池
第9-12行:是一些线程池的配置,在3.0之前最大线程并发数MAXIMUM_POOL_SIZE是一个固定值128,如今都是根据当前cpu合数来动态设置的,例如如今的床铺是四核的,因此MAXIMUM_POOL_SIZE为9,而且将阻塞线程由之前的10个变成如今的128个(记得前不久面试的时候面试官就问道为何会设置128这个数值为何不是129或者140,到如今我仍是不知道怎么回答这个问题),那么是否是意味着咱们并发的线程数超过137个以后,咱们的程序就会抛异常,其实不是这样的,咱们以前的SerialExecutor 类中的execute里面的代码
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); } }
当存在10个任务的时候,第一个任务进来,mActive为空执行scheduleNext,在scheduleNext方法中取出线程头部添加到线程池中,而后复制给mActive,当第二个任务进来时,mActive不为空,也就是说不执行scheduleNext()方法,因此只有等到第一个任务run方法执行完以后调用finally中的scheduleNext()才会执行下一个任务,因此来讲其实仍是单线程线性执行,一个接一个。
ok,到这里咱们基本上完成了对源码的解读,可是咱们仍是有几个疑问,咱们的onProgressUpdate()和publishProgress()怎么没有执行啊,onProgressUpdate方法只有在调用publishProgress()以后才执行,因此让咱们来看看publishProgress()的源码
@WorkerThread protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
很熟悉,又发现了咱们的handle,继续找到咱们的消息接受的地方
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; } } }
这不是咱们刚刚看过的代码,没错,上面我叫你们留意过的MESSAGE_POST_PROGRESS就是这个用于更新咱们进度的,在里面直接调用onProgressUpdate()方法。
ok,咱们如今仍是有一个疑问,就是为何说doInBackground运行在子线程中
咱们进过上面的源码分析指导doInBackground是在mWorker的call方法中调用的,因此咱们如今只须要在哪里调用了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; } };
因为咱们知道mWorker是以参数传递到mFuture中的,因此咱们仍是要看FutureTask这个类的源码
public class FutureTask<V> implements RunnableFuture<V> { public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } 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); } } }
这里我只展现除了重要的几个方法,咱们知道mFuture是以参数传递到SerialExecutor类中的execute方法中
public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } }
因此这里new了一个runnable建立了一个子线程,并调用mFuture的run方法,而在mFuture的run方法中有一行result = c.call();这里的c就是咱们的mWorker,因此这也就解释了咱们的doInBackground是运行在子线程中。
ok,到这里咱们的问题解决的差很少了,可是有一些公司喜欢问一下3.0以前的asynctask 的缺陷问题,在这里给你们找了一写,就直接贴出来了,够你们应付面试,具体是怎么产生的就你们本身去研究了,篇幅有点长,就不过多介绍了。
若是如今你们去面试,被问到AsyncTask的缺陷,能够分为两个部分说,在3.0之前,最大支持128个线程的并发,10个任务的等待。在3.0之后,不管有多少任务,都会在其内部单线程执行;
这是GitHub代码,有须要的同窗能够去下载一下.在写线程池这一块感受本身不是很熟悉,因此下一篇打算来总结总结线程池。