【Android源码解析】从源码角度深入分析AsyncTask原理
2018-03-20 00:14
1006 查看
一. 什么是AsyncTask?
是Android提供的轻量级的异步类,封装了线程池和handler。它是个抽象类,使用时需要去继承它,然后去实现异步操作,并反馈异步执行的结果。二. AsyncTask使用方法
首先先来看个例子,这是之前写过的一个gif图片下载的异步任务。使用AsyncTask要注意它的三个参数和五个方法。1. 三个参数
第一个,代表执行AsyncTask传入的参数;
第二个,代表执行后台任务的进度;
第三个,代表执行完任务返回的结果。
2. 五个方法
(1)onPreExecute:在ui线程调用,后台操作执行前调用;经常用于显示一个进度条。
(2)doInBackground:做的是耗时操作,并返回执行结果。同时,可以在doInBackground中调用publishProgress来发布进度单位。
(3)onPostExecute:接受执行结果。
(4)onProgressUpdate:当publishProgress调用完该方法被执行,一般用于控制进度条的进度。
public class GifImageDownLoader extends AsyncTask<String, Void, File> { private static final String foldPath = Environment.getExternalStorageDirectory() + "/CTRIP/cache/"; private ICallBack<File, String> mCallBack; public GifImageDownLoader(ICallBack<File, String> callBack) { this.mCallBack = callBack; } @Override protected File doInBackground(String... params) { String[] url = params[0].split("/"); String gifName = url[url.length - 1]; File f = new File(foldPath + gifName); if (!f.exists()) { f = downLoadGifImage(params[0], gifName); } return f; } @Override protected void onPostExecute(File f) { if (isCancelled() || f == null) { mCallBack.fail(null); return; } mCallBack.success(f, null); } private File downLoadGifImage(String gifUrl, String gifName) { File gifImageFile = null; InputStream is = null; FileOutputStream fos = null; try { URL downLoadUrl = new URL(gifUrl); HttpURLConnection conn = (HttpURLConnection) downLoadUrl.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); if (conn.getResponseCode() == 200) { File fold = new File(Environment.getExternalStorageDirectory() + "/CTRIP/cache"); if (!fold.exists()) { fold.mkdirs(); } is = conn.getInputStream(); gifImageFile = new File(foldPath, gifName); fos = new FileOutputStream(gifImageFile); int len = 0; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } fos.flush(); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null) { is.close(); } if (fos != null) { fos.close(); } } catch (Exception e) { e.printStackTrace(); } } return gifImageFile; } }
三. AsyncTask内部原理
Android主线程肩负绘制UI界面和即使响应用户操作的重任,所以为了避免用户点击按钮没有反应,应该把耗时操作放在子线程进行。1. AsyncTask本质是一个静态的线程池,其子类实现的异步任务都提交到静态线程池中进行。
2. 线程池的工作线程执行doInBackground(mParams)方法执行异步任务。
3. 当任务状态改变后,子线程向UI线程发送消息,AsyncTask内部的handler相应这些消息,并调用相关回调函数。
四. 线程池ThreadPoolExecutor
创建线程池一般不直接new一个线程池对象,因为需要配置一堆东西,我们来看下其构造方法:所以官方也不推荐我们这样创建线程池,而是推荐使用Executors的工厂方法来创建线程池,Executors是官方提供的一个工厂类,它里面封装了众多功能的线程池,从而使得我们创建线程池非常简便,主要提供了五种线程池:
1. newFixedThreadPool()
创建一个固定线程数量的线程池,该线程池中的线程数量都不变,即不会创建新的线程,也不会销毁已经创建好的线程,自始至终都是那几个固定的线程工作。
如果有空闲线程则处理任务,如果没有则将任务放到一个队列中,一旦线程空闲了则按FIFO的方式处理队列中的任务。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
2. newCachedThreadPool()
该线程池中的线程数量不固定,可根据实际情况调整。
如果有新任务提交,当没有空闲线程则创建新线程,如果有空闲线程则复用该空闲线程,当空闲线程空闲时间超过配置的等待时间则被销毁。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
3. newSingleThreadPool()
只有一个线程的线程池。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
4. newScheduledThreadPool()
可定时执行某任务的线程池。
public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory) { return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); }
5. newSingleThreadScheduledPool()
只有一个线程的可定时执行某任务的线程池。
可以看到工厂方法最终都是通过ThreadPoolExecutor来创建线程池。在它的构造方法的参数中,最重要的是BlockingQueue任务队列,不同线程池的任务队列实现方法肯定不同。
而线程池的使用也很简单,当创建好线程池后,只需要调用其execute方法即可。
AsyncTask源码解析
AsyncTask的源码其实非常短,只有700多行。我们先来看AsyncTask的构造方法。AsyncTask构造方法
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); } } }; }
workRunnable是一个Callable,大家可能对Callable不熟悉,它其实跟Runnable使用非常像,只不过Callable有返回值而Runnable没有。
FutureTask是Callable的包装类。
execute方法
execute方法其实执行的是executeOnExecutor方法。public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } 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; }
可以看到executeOnExecutor传入的第一个参数是sDefaultExecutor,这是一个线程池。我们稍后来分析这个线程池,我们先来看下executeOnExecutor里都做了什么。
1. 首先是判断AsyncTask当前的执行状态,只有当前状态是Status.PENDING才可继续使用;
2. 接着设置当前状态为RUNNING状态。
3. 然后调用onPreExecute方法。
4. 接着为mWorker设置参数,并执行该任务。
AsyncTask中线程池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方法加上了synchronized修饰,从而保证AsyncTask中的任务是串行执行的。
五. 内部handler
当任务执行完通过internalHandler讲执行结果同步给主线程。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; } } }
六. 最后
以上就是我对AsyncTask源码的分析,后续当我有新的理解时还会继续更新。至于为什么想到要去看AsyncTask的源码了呢?主要是因为最近想要改进一下我们的缓存机制,希望能在合适的时机清理一下缓存,我们用AsyncTask比较多,也知道它是封装了线程池和handler,但具体怎么实现的还真没有系统地读过。好了,时间不早了,现在是夜里十二点十五,大家晚安。如有任何疑问可以邮件联系我zhshan@ctrip.com。
相关文章推荐
- 【Android 源码解析】从源码角度深入分析Handler消息机制
- Android 带你从源码的角度解析Scroller的滚动实现原理
- Android AsyncTask完全解析,带你从源码的角度彻底理解
- Android 带你从源码的角度解析Scroller的滚动实现原理
- Android 带你从源码的角度解析Scroller的滚动实现原理
- Android 带你从源码的角度解析Scroller的滚动实现原理
- Android 带你从源码的角度解析Scroller的滚动实现原理
- Android AsyncTask完全解析,带你从源码的角度彻底理解
- Android 带你从源码的角度解析Scroller的滚动实现原理
- 【Android】带你从源码的角度解析Scroller的滚动实现原理
- 【android】从源码的角度深入分析Scroller
- Android 带你从源码的角度解析Scroller的滚动实现原理
- Android AsyncTask运作原理和源码分析
- android动画之从源码角度分析动画原理
- Android 带你从源码的角度解析Scroller的滚动实现原理
- Android AsyncTask完全解析,带你从源码的角度彻底理
- Android AsyncTask完全解析,带你从源码的角度彻底理解
- Android AsyncTask运作原理和源码分析
- Android AsyncTask完全解析,带你从源码的角度彻底理解
- Android 带你从源码的角度解析Scroller的滚动实现原理