首页 > 代码库 > 源码分析Android AsyncTask
源码分析Android AsyncTask
Android UI操作是线程不安全的,若想在子线程红进行UI操作,需要借助Android提供的Handler。Android提供的AsyncTask其实是对Handler的封装,方便我们在子线程中更新UI元素。AsyncTask是一个泛型类,书写格式为: AsyncTask<Params, Progress, Result>。这三个参数的用途如下:
AsyncTask是个抽象类,有四个方法需要被重写,这四个方法的的调用过程如下:
第1步. onPreExecute()方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作。
第2步. doInBackground(Params...)方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回。如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
第3步. 当在后台任务中调用了publishProgress(Progress...)方法后,onProgressUpdate(Progress...)方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
第4步. 当后台任务执行完毕并通过return语句进行返回时,onPostExecute(Result)很快会被调用。此处可以
进行UI操作。
定时调用onProgressUpdate(Progress... values)的效果。publishProgress(Progress... values)的源码如下:
由上可知sDefaultExecutor先调用mFuture的run(), 而mFuture的run()又会调用mWorker的call()。当执行完一个任务后,mFuture的done()会被回调。done()会调用ppostResultIfNotInvoked(Result result),源码如下:
1. Params
这三个参数的用途如下:
1. Params
在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
2. Progress
后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
3. Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
Android SDK中给出的一个AsyncTask用法如下:
private class DownloadFilesTask extends AsyncTask(URL, Integer, Long) { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } new DownloadFilesTask().execute(url1, url2, url3);
AsyncTask是个抽象类,有四个方法需要被重写,这四个方法的的调用过程如下:
第1步. onPreExecute()方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作。
第2步. doInBackground(Params...)方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回。如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
第3步. 当在后台任务中调用了publishProgress(Progress...)方法后,onProgressUpdate(Progress...)方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
第4步. 当后台任务执行完毕并通过return语句进行返回时,onPostExecute(Result)很快会被调用。此处可以
进行UI操作。
AsyncTask的源码
当初使用AsyncTask的时候,一直不明白onProcessUpdate为何会定期被调用。后来在工作中要实现一个定时更新任务进度的功能,我们用的是一个Handler给自己发送消息来进行定时刷新。现在研究了一下AsyncTask的实现原理,发现它也是使用Handler给自己发送消息的原理来实现定时刷新。当我选用的是Android 4.4的源码,若大家能搞懂AsyncTask的实现原理,也能自己写一个简单的AsyncTask。
首先看看构造函数:注意,构造函数一定要在UI中调用。
/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */ 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); } } }; }这个函数初试化了两个变量:mWorker和mFuture。mWorker是一个Callable对象,mFuture是一个实现了Runnable和Future的对象。启动AsyncTask调用的是execute(Params)
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }该方法调用了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; }该方法先设置运行状态,然后调用onPreExecute,最后调用exec.execute(mFuture).传入的exec是sDefaultExecutor, sDefaultExecutor是一个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); } } }当调用sDefaultExecutor.execute(runnable),该方法用一个ArrayDeque来保存runnable,runnable会在合适的时候被调用。第一次运行mActivity等于null了,于是会调用scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。这里使用了一个try finally代码块,并在finally中调用了scheduleNext()方法,每次当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。
sDefaultExecutor.execute(Runnable)传入的对象是mFuture,mFuture是一个FutureTask对象,其构造函数如下所示:
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }由AsyncTask的构造函数可知,FutureTask中的callable是mWorkder对象。FutureTask的run()代码如下所示:
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }因此,最终会调用mWorker的call()。mWorker的call()位于AsyncTask的构造函数中,大家可以发现其先调用doInBackgroud(Params),然后调用postResult().postResult(Result)的源代码如下:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }sHandler是一个InternalHandler,代码如下:
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; } } }当传入的是一条MESSAGE_POST_RESULT消息,就会去执行AsyncTask.finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。当我们在doInBackgroud(Params)中利用for循环来调用publishProgress(Progress... values),就会模拟出
定时调用onProgressUpdate(Progress... values)的效果。publishProgress(Progress... values)的源码如下:
protected final void publishProgress(Progress... values) { if (!isCancelled()) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }它首先会判断线程是否已经结束,若没结束,则sHandler给自己发送一个消息,消息的类型为MESSAGE_POST_PROGRESS,sHandler.handleMessage(Message)则会调用onProgressUpdate(Progress... values)。
由上可知sDefaultExecutor先调用mFuture的run(), 而mFuture的run()又会调用mWorker的call()。当执行完一个任务后,mFuture的done()会被回调。done()会调用ppostResultIfNotInvoked(Result result),源码如下:
private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { postResult(result); } }postResult(Result)已经在前面有过讲解,在此不再赘述。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。