深入理解AsyncTask
众所周知,在Android中如果要执行耗时的操作,一般是在子线程中处理,使用new Thread的方法实现是最常见的方法之一。今天,我们要讲的是另外一个,Android提供的异步任务类AsyncTask,底层是使用线程池实现的。
一、Android的线程
线程是操作系统的最小执行单位,它的创建和销毁都会消耗一定的系统资源,如果频繁的创建和销毁,显然不是高效的做法,正确的做法是,采用线程池,缓存一定量的线程,通过复用这些线程,避免造成极大的系统开销。
二、AsyncTask
这是一个抽象类,使用方便,代码简洁,所以说是一个轻量级的异步类。它可以在线程池中执行后台任务,然后把执行的进度和结果通过Handler传给UI线程进而刷新视图。
该类的声明如下:
public abstract class AsyncTask<Params, Progress, Result>
其中各个参数的含义如下:
Params:开始异步任务执行时传入的参数类型;
Progress:异步任务执行过程中,返回下载进度值的类型;
Result:异步任务执行完成后,返回的结果类型;
如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。
三、AsyncTask源码
AsyncTask内部封装了2个线程池:SerialExecutor和THREAD_POOL_EXECUTOR,和1个Handler(IntentHandler)。其中SerialExecutor线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR线程池才真正地执行任务,InternalHandler用于从工作线程切换到主线程。部分源码如下:
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); } } }
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 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; } } }
四、注意事项
1、因为执行后的结果要传递到主线程,所以使用Handler进行工作线程和主线程的切换,所以AsyncTask创建实例和execute需要在主线程调用;
2、onPreExecute(),onProgressUpdate(Progress... values),onPostExecute(Result result),onCancelled()方法是在主线程执行的,而doInBackground(Params... params)是在工作线程执行;
3、执行顺序:onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute(),如果不需要执行更新进度则为onPreExecute() --> doInBackground() --> onPostExecute(),其中publishProgress方法在doInBackground方法中调用,会触发onProgressUpdate方法;
4、AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。
5、一个任务实例只能执行一次,如果执行第二次将会抛出异常。
五、AsyncTask使用不当的后果
1、生命周期:AsyncTask不与任何组件绑定生命周期,所以在Activity或者Fragment中创建执行AsyncTask时,最好在Activity或Fragment的onDestory()调用 cancel(boolean);
2、内存泄漏:如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露;
3、 结果丢失:屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
参考链接:
http://www.jianshu.com/p/817a34a5f200