咱们知道,Android应用的主线程(UI 线程)肩负着绘制用户界面和及时响应用户操做的重任,为了不“用户点击按钮后没反应”这样的糟糕用户体验,咱们就要确保主线程时刻保持着较高的响应性。为了作到这一点,咱们就要把耗时的任务移出主线程,那么耗时的任务交给谁来完成呢?答案就是工做者线程。Android开发中咱们一般让主线程负责前台用户界面的绘制以及响应用户的操做,让工做者线程在后台执行一些比较耗时的任务。Android中的工做者线程主要有AsyncTask、IntentService、HandlerThread,它们本质上都是对线程或线程池的封装。对线程和线程池还不太熟悉的小伙伴请参考文末“延伸阅读”部分给出的连接。html
总的来讲,咱们使用工做者线程是由于主线程已经有不少活要干了,累活就得交给别人干。AsyncTask是咱们平常中普遍使用的一种工做者线程,它的方便之处在于能够在后台任务执行完毕时根据返回结果相应的更新UI。下面咱们来研究一下它的工做原理。java
AsyncTask是对Handler与线程池的封装。使用它的方便之处在于可以更新用户界面,固然这里更新用户界面的操做仍是在主线程中完成的,可是因为AsyncTask内部包含一个Handler,因此能够发送消息给主线程让它更新UI。另外,AsyncTask内还包含了一个线程池。使用线程池的主要缘由是避免没必要要的建立及销毁线程的开销。设想下面这样一个场景:有100个只须要0.1ms就能执行完毕的任务,若是建立100个线程来执行这些任务,执行完任务的线程就进行销毁。那么建立与销毁进程的开销就极可能成为了影响性能的瓶颈。经过使用线程池,咱们能够实现维护固定数量的线程,无论有多少任务,咱们都始终让线程池中的线程轮番上阵,这样就避免了没必要要的开销。‘android
在这里简单介绍下AsyncTask的使用方法,为后文对它的工做原理的研究作铺垫,关于AsyncTask的详细介绍你们能够参考官方文档或是相关博文。缓存
AsyncTask是一个抽象类,咱们在使用时须要定义一个它的派生类并重写相关方法。AsyncTask类的声明以下:多线程
public abstract class AsyncTask<Params, Progress, Result>
咱们能够看到,AsyncTask是一个泛型类,它的三个类型参数的含义以下:ide
Params:doInBackground方法的参数类型;oop
Progress:AsyncTask所执行的后台任务的进度类型;post
Result:后台任务的返回结果类型。性能
咱们再来看一下AsyncTask类主要为咱们提供了哪些方法:this
onPreExecute() //此方法会在后台任务执行前被调用,用于进行一些准备工做 doInBackground(Params... params) //此方法中定义要执行的后台任务,在这个方法中能够调用publishProgress来更新任务进度(publishProgress内部会调用onProgressUpdate方法) onProgressUpdate(Progress... values) //由publishProgress内部调用,表示任务进度更新 onPostExecute(Result result) //后台任务执行完毕后,此方法会被调用,参数即为后台任务的返回结果 onCancelled() //此方法会在后台任务被取消时被调用
以上方法中,除了doInBackground方法由AsyncTask内部线程池执行外,其他方法均在主线程中执行。
AsyncTask的优势在于执行完后台任务后能够很方便的更新UI,然而使用它存在着诸多的限制。先抛开内存泄漏问题,使用AsyncTask主要存在如下局限性:
在Android 4.1版本以前,AsyncTask类必须在主线程中加载,这意味着对AsyncTask类的第一次访问必须发生在主线程中;在Android 4.1以及以上版本则不存在这一限制,由于ActivityThread(表明了主线程)的main方法中会自动加载AsyncTask
AsyncTask对象必须在主线程中建立
AsyncTask对象的execute方法必须在主线程中调用
一个AsyncTask对象只能调用一次execute方法
接下来,咱们从源码的角度去探究一下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); } } }; }
以上代码中,首先初始化了mWorker,它是一个派生自WorkRunnable类的对象。WorkRunnable是一个抽象类,它实现了Callable<Result>接口。咱们再来看一下call方法的定义,首先将mTaskInvoked设为true表示当前任务已被调用过,而后经过Process.setThreadPriority方法设置线程的优先级。接着会调用AsyncTask对象的doInBackground方法开始执行咱们所定义的后台任务,并获取返回结果存入result中。最后将任务返回结果传递给postResult方法。关于postResult方法咱们会在下文进行分析。由此咱们能够知道,实际上AsyncTask的成员mWorker包含了AyncTask最终要执行的任务(即mWorker的call方法)。
接下来让咱们看看对mFuture的初始化。咱们能够看到mFuture是一个FutureTask的直接子类(匿名内部类)的对象,在FutureTask的构造方法中咱们传入了mWorker做为参数。咱们使用的是FutureTask的这个构造方法:
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
也就是说,mFuture是一个封装了咱们的后台任务的FutureTask对象,FutureTask类实现了FutureRunnable接口,经过这个接口能够方便的取消后台任务以及获取后台任务的执行结果。想要进一步了解,请参考“延伸阅读”部分给出的连接。
从上面的分析咱们知道了,当mWorker中定义的call方法被执行时,doInBackground就会开始执行,咱们定义的后台任务也就真正开始了。那么这个call方法何时会被调用呢?咱们能够看到通过层层封装,其实是mFuture对象封装了call方法,当mFuture对象被提交到AsyncTask包含的线程池执行时,call方法就会被调用,咱们定义的后台任务也就开始执行了。下面咱们来看一下mFuture是何时被提交到线程池执行的。
首先来看一下execute方法的源码:
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
咱们能够看到它接收的参数是Params类型的参数,这个参数会一路传递到doInBackground方法中。execute方法仅仅是调用了executeOnExecutor方法,并将executeOnExecutor方法的返回值做为本身的返回值。咱们注意到,传入了sDefaultExecutor做为executeOnExecutor方法的参数,那么sDefaultExecutor是什么呢?简单的说,它是AsyncTask的默认执行器(线程池)。AsyncTask能够以串行(一个接一个的执行)或并行(一并执行)两种方式来执行后台任务,在Android3.0及之后的版本中,默认的执行方式是串行。这个sDefaultExecutor就表明了默认的串行执行器(线程池)。也就是说咱们日常在AsyncTask对象上调用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; }
从以上代码中咱们能够知道,当AsyncTask对象的当前状态为RUNNING或FINISHED时,调用execute方法会抛出异常,这意味着不能对正在执行任务的AsyncTask对象或是已经执行完任务的AsyncTask对象调用execute方法,这也就解释了咱们上面提到的局限中的最后一条。
接着咱们看到上面的代码中存在一个对onPreExecute方法的调用,这表示了在执行后台任务前确实会调用onPreExecute方法。
接着往下看,咱们传入的execute方法的params参数赋值给了mWorker的mParams成员变量;然后在调用了exec的execute方法,并传入了mFuture做为参数。exec就是咱们传进来的sDefaultExecutor。那么接下来咱们看看sDefaultExecutor到底是什么。在AsyncTask类的源码中,咱们能够看到这句:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
sDefaultExecutor被赋值为SERIAL_EXECUTOR,那么咱们来看一下SERIAL_EXECUTOR:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
如今,咱们知道了实际上sDefaultExecutor是一个SerialExecutor对象,咱们来看一下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); } } }
咱们来看一下execute方法的实现。mTasks表明了SerialExecutor这个串行线程池的任务缓存队列,首先,咱们用offer方法向任务缓存队列中添加一个任务,任务的内容如接下来的的run方法定义所示。咱们能够看到,run方法中:先是调用了mFuture(execute方法的参数r就是咱们传入的mFuture)的run方法,而mFuture的run方法内部会调用mWorker的call方法,而后就会调用doInBackground方法,咱们的后台任务也就开始执行了。那么咱们提交到任务缓存队列中的任务何时会被执行呢?咱们接着往下看。
首先咱们看到SerialExecutor类中定义了一个Runnable变量mActive,它表明了当前正在执行的AsyncTask对象。execute方法中会判断mActive是否为null,若为null,就调用scheduleNext方法。在scheduleNext方法中,若缓存队列非空,则调用THREAD_POOL_EXECUTOR.execute方法执行从缓存队列中取出的任务,这时咱们的后台任务便开始你真正执行了。
经过以上的分析,咱们能够知道SerialExecutor所完成的工做主要是把任务加到任务缓存队列中,而真正执行任务的是THREAD_POOL_EXECUTOR。咱们来看下THREAD_POOL_EXECUTOR是什么:
从上面的代码咱们能够知道,它是一个线程池对象。根据AsyncTask的源码,咱们能够获取它的各项参数以下:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; 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()); } };
由以上代码咱们能够知道:
corePoolSize为CPU数加一;
maximumPoolSize为CPU数的二倍加一;
存活时间为1秒;
任务缓存队列为LinkedBlockingQueue。
如今,咱们已经了解到了从咱们调用AsyncTask对象的execute方法开始知道后台任务执行完都发生了什么。如今让咱们回过头来看一看以前提到的postResult方法的源码:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } }
在以上源码中,先调用了getHandler方法获取AsyncTask对象内部包含的sHandler,而后经过它发送了一个MESSAGE_POST_RESULT消息。咱们来看看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; } } }
从以上代码中咱们能够看到,sHandler是一个静态的Handler对象。咱们知道建立Handler对象时须要当前线程的Looper,因此咱们为了之后可以经过sHandler将执行环境从后台线程切换到主线程(即在主线程中执行handleMessage方法),咱们必须使用主线程的Looper,所以必须在主线程中建立sHandler。这也就解释了为何必须在主线程中加载AsyncTask类,是为了完成sHandler这个静态成员的初始化工做。
注:这块描述我的观点和原博主不同,以上InternalHandler源码为5.0之后的,能够看到在初始化时,传入的Looper.getMainLooper(),因此是能够在线程中建立AsyncTask的。但在5.0之前是不能够的。下面是5.0前的源码。
private static class InternalHandler extends Handler { @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方法中,咱们能够看到,当sHandler收到MESSAGE_POST_RESULT方法后,会调用finish方法,finish方法的源码以下:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
首先经过调用isCancelled方法判断AsyncTask任务是否被取消,若取消了则调用onCancelled方法,不然调用onPostExecute方法;接着把mStatus设为FINISHED,表示当前AsyncTask对象已经执行完毕。
通过了以上的分析,咱们大概了解了AsyncTask的内部运行逻辑,知道了它默认使用串行方式执行任务。那么如何让它以并行的方式执行任务呢? 阅读了以上的代码后,咱们不可贵到结论,只需调用executeOnExecutor方法,并传入THREAD_POOL_EXECUTOR做为其线程池便可。
1. Java核心技术点之多线程: http://www.cnblogs.com/absfree/p/5327678.html
2. 深刻理解Java之线程池: http://www.cnblogs.com/absfree/p/5357118.html
3. Callable&Future&FutureTask: http://www.cnblogs.com/dolphin0520/p/3949310.html