AsyncTask源码浅析

来源:互联网 发布:78团淘宝兼职是真的吗 编辑:程序博客网 时间:2024/06/11 11:28

AsyncTask是Android的异步类……恩,大家应该都知道,直接分析吧。开始之前如果对Android的Callable,Future不了解的话可以先看看这篇文章 Java并发编程:Callable、Future和FutureTask

首先看看简单的用法,我自定义了一个TestAsyncTask:

class TestAsyncTask extends AsyncTask<String, Integer, Long> {    @Override    protected void onPreExecute() {        super.onPreExecute();    }    @Override    protected void onPostExecute(Long aLong) {        super.onPostExecute(aLong);    }    @Override    protected Long doInBackground(String... params) {        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return null;    }}

这里简单说一下,这里用到了三个泛型,第一个代表我们执行的任务需要的参数,就是doInBackground里面的参数,第二个泛型是用来更新进度的时候使用的,第三个泛型用来指定返回类型。

用的时候呢new TestAsyncTask().execute("helloWorld");

那么就从new开始分析先看一下AsyncTask的构造函数:

 public AsyncTask() {        //创建一个WorkerRunnable,这个对象实现了Callable接口        mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                mTaskInvoked.set(true);                  Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                //noinspection unchecked                 //执行doInBackground                Result result = doInBackground(mParams);                Binder.flushPendingCommands();                return postResult(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);                }            }        };    }

首先先创建了一个创建一个WorkerRunnable对象,看源码可以知道这个对象实现了Callable接口,并且把任务封装在了call方法里面,这个方法主要就是执行了doInBackground方法.
然后从、又创建了一个FutureTask对象,看过上面那个文章的应该知道FutureTask既可以当Runnable使用又可以当Future使用。new FutureTask<Result>(mWorker),这里的Result就是任务执行完之后的返回值的类型,其实FutureTask只是包装了一下mWorker,所以我们要执行的任务就是FutureTask类,主要任务就是执行doInbackground,并把执行结果交给Future

下面继续看execute(“helloWorld”)方法:

 public final AsyncTask<Params, Progress, Result> execute(Params... params) {        return executeOnExecutor(sDefaultExecutor, params);    }

这个方法实际上是调用了executeOnExecutor方法,我们一会再看这个方法,先看一下传入这个方法的两个参数:sDefaultExecutor,和params

先看看sDefaultExecutor的定义:

private static volatile Executor sDefaultExecutor = 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);            }        }    }

可以看出SerialExecutor 中有一个任务队列mTasks,每次执行SerialExecutorexecute方法都会将任务加入这个任务队列,如果当前没有任务在执行,那么久取出一个任务让THREAD_POOL_EXECUTOR执行。THREAD_POOL_EXECUTOR比较简单,

params比较简单,就是new TestAsyncTask().execute("helloWorld");的时候传入的“hello world”

 public static final Executor THREAD_POOL_EXECUTOR            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);              

可以看出THREAD_POOL_EXECUTOR只是一个常量池。

这样SerialExecutor 的任务就清楚了,将消息放入消息队列,并一个一个地串行地取出并交给THREAD_POOL_EXECUTOR执行

下面接着分析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()        onPreExecute();        //把需要的参数传给mWorker,也就是传给FutherTask即mFuture        mWorker.mParams = params;        //这里的exec就是我们在execute方法中传给executeOnExecutor的第一个参数也就是sDefaultExecutor        exec.execute(mFuture);        return this;    }

这段代码首先判断任务是否正在执行或者已经执行完了,如果是,那就抛出异常,否则执行onPreExecute();然后把我们在外面调用execute("hello world")传入的参数hello world参数传入mWorker即要执行的任务,然后调用sDefaultExecutorexecute方法。之后就像上面介绍的sDefaultExecutor先把任务加入任务队列然后串行的执行任务,任务的主要内容就是执行doInBackground并把返回的Result传入postResult(result);

分析到这里,我们已经可以知道doInBackground就是在THREAD_POOL_EXECUTOR线程池的子线程中执行的。

最后再来看一下postResult(result).方法:

 private Result postResult(Result result) {        @SuppressWarnings("unchecked")        //把result放到message中并发送到getHandler        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,                new AsyncTaskResult<Result>(this, result));        message.sendToTarget();        return result;    }

这里可以看到,就是把result放到message中并把getHandler得到的handler作为message的target,把message的what设置成MESSAGE_POST_RESULT表示这个消息代表任务执行的结果。看一下getHandler:

 private static Handler getHandler() {        synchronized (AsyncTask.class) {            if (sHandler == null) {                sHandler = new InternalHandler();            }            return sHandler;        }    }

getHandler只是返回了一个InternalHandler

        //使用的是UI线程的Looper和消息队列        public InternalHandler() {            super(Looper.getMainLooper());        }        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})        @Override        public void handleMessage(Message msg) {            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;            switch (msg.what) {                    //what是MESSAGE_POST_RESULT,那么就处理doInbackground返回的结果                    case MESSAGE_POST_RESULT:                    // There is only one result                   // finish内部会调用我们实现的onPostExecute方法                    result.mTask.finish(result.mData[0]);                    break;                    //what是MESSAGE_POST_PROGRESS,那么就调用onProgressUpdate来更新progress                case MESSAGE_POST_PROGRESS:                    result.mTask.onProgressUpdate(result.mData);                    break;            }        }    }

上面的注释写得很明白了,根据消息的不同,使用handler在UI线程中处理结果,或者更新progerss。
看看finish和的代码

 private void finish(Result result) {        if (isCancelled()) {            onCancelled(result);        } else {            //onPostExecute就是在这里执行的            onPostExecute(result);        }        mStatus = Status.FINISHED;    }    protected final void publishProgress(Progress... values) {        if (!isCancelled()) {            //publishProgress把what为MESSAGE_POST_PROGRESS的消息发送给了           // InternalHandler,所以更新了progress            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();        }    }

看到这里可以总结一下,doInBackground方法是在线程池中的线程中执行,而onPreExecute是在UI线程中执行,而onPostExecute和onProgressUpdate是用过InternalHandler 从而在UI线程执行的。而且一个AsyncTask对象执行结束之后也就没用了,一个AsyncTask对象只能execute一次,否则会报异常,如果还想执行一个任务需要重新创建一个AsyncTask对象,这一点可以从executeOnExecutor的代码看出来

到这里就算大致的分析完了,其实还有许多细节没有提到,感觉自己没有看源码光看别人的文章的话还是很难理解,最好没事的话多翻翻源码,AsyncTask这个类的源码都在一个文件里面也不多。

0 0