记录下AsyncTask的学习心得:
AsyncTask类是用来处理异步请求的,当我们在UI线程(主线程)中执行耗时操作时(从网络获取数据,从数据库中获取数据,或者读取文件)会阻塞主线程的运行,导致界面不流畅,卡。这种情况下,我们需要将这些耗时的操作另起一个线程来执行,尽量在主线程中执行UI操作。
一般做法,我们用Thread+handler去处理耗时操作,和处理善后工作。AsyncTask对Thread和Handler进行了一定的封装,简化代码的编写,方便我们使用。
AsyncTask的架构是创建一个Executor来执行ArrayDeque里的Runnable对象,这个Executor可以是自定义的,也可以是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实现了Executor中的execute方法,execute中将一个Runnable对象(在本类中是一个FutureTask)加入队列中,然后判断当前是否有活动的Runnable,如果没有的话,就从Queue中take一个,然后将它设为当前活动Runnable, 并且执行这个Runnable。
现在来看一下则个Runnable做了什么,首先他执行参数Runnable的run方法,finally去执行sheduleNext()函数,进入这个函数,他拉去队列的首元素出来当成当前active Runnable,如果不为空,就执行他,这样Executor就顺序执行了Queue里的Runnable对象。当然还可以自定义自己的Executor,比如给队列赋予权重,权重大的runnable先得到执行
PriorityBlockingQueue<PriorityRunnable> mTasks=new PriorityBlockingQueue<PriorityRunnable>(); private static class PriorityRunnable implements Comparable<PriorityRunnable>{ private Runnable mRunnable; private Priority mPriority; private Integer mSequence; private AtomicInteger mSequenceGenerator = new AtomicInteger(); public PriorityRunnable(final Runnable r){ mRunnable=new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }; this.setmSequence(getSequenceNumber()); // this.setmPriority(); } public int getSequenceNumber() { return mSequenceGenerator.incrementAndGet(); } public Runnable getRunnable(){ return mRunnable; } public void setmRunnable(Runnable mRunnable) { this.mRunnable = mRunnable; } public Priority getmPriority() { return mPriority; } public void setmPriority(Priority mPriority) { this.mPriority = mPriority; } public Integer getmSequence() { return mSequence; } public void setmSequence(Integer mSequence) { this.mSequence = mSequence; } @Override public int compareTo(PriorityRunnable another) { // TODO Auto-generated method stub Priority left = this.getmPriority(); Priority right = another.getmPriority(); return left == right ? this.mSequence - another.mSequence : right.ordinal() - left.ordinal(); } }
上面的Runnable是自定义的,实现了compareable接口,因为我们用PriorityBlockingQueue来容纳Runnable。
上面说到execute参数里的Runnable是个FutureTask,FutrueTask是用来执行异步计算的,它可以包装一个Callable或者Runnable,并且获得Callable的计算结果和运行状态。在AsyncTask中,每一次耗时请求都new一个全新的FutureTask。FutureTask的初始化工作在 new AsyncTask()中。
public AsyncTask() { mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; 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 occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } }; }
在AsyncTask初始化中,首先先new 一个WorkRunnable,WorkRunnable实现了Callable<Result>的抽象类,在call方法体内,首先mTaskInvoked.set(true),这个mTaskInvoked是个AutomicBoolean类型的变量,用来追踪Task的状态,当当前Task被执行是,将状态置为true,用来判断Task是否被取消执行。设置完mTaskInvoked后,又为该Task设置了线程优先级为Process.THREAD_PRIORITY_BACKGROUND级别,最后返回postResult函数值,进入postResult():
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
postResult函数参数为doInBackground()函数的执行结果,这里的doInBackground()函数为AsyncTask的抽象方法,我们需要在自定义的AsyncTask类中去实现这个函数,我们的异步请求的主要逻辑就在这里执行。doInBackground()函数的返回值类型是Result,Result是泛型类型,postResult()函数用来创建一个Message,然后将Result包装为AsyncTaskResult类型,然后由Message携带传给相应线程的Handler去处理。看一下AsyncTaskResult的数据结构:
private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }
它有两个字段,mTask和mDate, mTask用来保存自己的引用,mData用来保存mFuture.get()的值,这个类的用法下面会进一步分析。
new WorkRunnable后接着new FutureTask,并且mWorker作为参数传入FutureTask作为其获取数据的来源。FutureTask可以控制线程的执行和得到线程返回的数据,当FutureTask中的Callable执行完后,FutureTask的done函数得以触发。如果线程在执行前就被Cancle掉,那么直接执行FutureTask的done函数。FutureTask的状态是isDone,postResultIfNotInvoked()函数主要是用来处理task被cancel的情况,当Task被cancel时,WorkRunnable的call函数不会执行,此时mTaskInvoked的值为false,然后执行postResult函数,这样保证了Task不管是被执行还是被cancel都能得到处理。
private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { postResult(result); } }
现在来看InternalHandler,这是个Handler的子类,他接受Message传来的数据,根据msg.what进行判断以进行不同的逻辑处理。在这里msg.what有两种类型,一种是MESSAGE_POST_RESULT,表示处理mWorkRunnable执行完毕发送的message,执行AsyncTask的finish函数。另一种是MESSAGE_POST_PROGRESS,它表示处理mWorkRunnable在更新时发送的message,执行AsyncTask的onProgressUpdate函数.
finish函数:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
函数体先判断当前task是否被cancelled,如果isCancelled,执行onCacelled(result)函数。
onCancelled函数:
protected void onCancelled(Result result) { onCancelled(); } protected void onCancelled() { }
我们可以override这个函数,执行自己的操作。
如果当前task没有被cancelled掉,执行onPostExecute(result)函数,同样我们可以在AsyncTask的子类中override这个函数,这个函数是在UI线程中执行的,可以对UI元素进行操作。
在这里出现了一个变量mStatus,看它的定义:
public enum Status { /** * Indicates that the task has not been executed yet. */ PENDING, /** * Indicates that the task is running. */ RUNNING, /** * Indicates that {@link AsyncTask#onPostExecute} has finished. */ FINISHED, }
它有三种状态,一种是Pendding,注释明明白白的说道,这是为了标识还未执行的tasks的状态,Running用来标识正在运行的task的状态,Finished用来标识已经执行完任务的task的状态。
现在再看看到底AsyncTask是怎么执行的,我们平常用的时候是这样子的 new MyAsyncTask().executef();
看看execute函数的庐山真面目吧:
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; }
首先,函数体先判断当前task的状态,如果状态不是未运行,则抛出异常,如果执行execute()时,当前task处于Running状态,则抛出“cannot execute task:the task is already running"异常,
至于为什么要这么设计,我的猜想是:
Future 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。
只要是重新new AsyncTask(),就会获得一个全新的task。
回到代码,如果当前task的状态是pending,那么更新它的状态为Running。然后执行onPreExecute(),函数,它和onPostExecute还有onBackground()函数一样,都可以在客户端override,定义自己的业务逻辑。执行完毕后,将从客户端传来的可变参数params赋值给mWorkRunnable的mParams成员变量,用以保存。最后开始调用线程池的execute()函数,之前说过execute()函数将当前task队列加入queue队列中,如果没有正在执行的task,则执行刚创建的task,如果有正在执行任务的task,则先缓存在队列中,等待他执行的时机。
最后看一下线程池:
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>(10); public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
ThreadFactory类是用来创建线程的,简单的重写newThread方法即可定义自己的ThreadFactory。
ThreadPoolExecutor类有几个重要的参数,core_pool_size:池中所保存的线程数,包括空闲线程。,maximum_pool_size:池中允许的最大线程数,keep_alive:当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。unit - keepAliveTime 参数的时间单位。workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。threadFactory - 执行程序创建新线程时使用的工厂。
对于AsycnTask的大体分析就到此为止了。谈一下自己对AsycnTask的几点感想:
1.AsycnTask在SerialExecutor设计时用到了设计模式中的责任链模式。
2.频繁的获取网络数据或者进行耗时操作时,new AsycnTask()可能造成大量的临时对象,影响性能,导致界面”打嗝“性卡顿。
3.AsycnTask不适合上传大文件,一般上传大文件用socket上传,或者断点上传,利用RandomAceccsFile来实现。
4AsycnTask利用线程池来管理Thread,在性能上比每次单独创建一个Thread更省内存。
以上有不对的地方,敬请堪正。
关于ThreadPoolExecutor,点这http://blog.sina.com.cn/s/blog_70bcd7c10101a1w7.html