【Android】AsyncTask原理应用及源码关键部分解析
来源:互联网 发布:测评软件 编辑:程序博客网 时间:2024/05/17 01:25
为了更加方便我们在子线程中更新UI元素,Android从1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程。AsyncTask是android提供的轻量级的异步类,自定义的异步功能类可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的进度,最后反馈执行的结果给UI主线程
AsyncTask的简单使用示例
使用AsyncTask的时候经常需要覆写的四个方法:
onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。doInBackground(Params…)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成。onProgressUpdate(Progress…)
当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等
简单的AsyncTask的使用示例代码:
/* * 异步任务AsyncTask使用示例 */ public class MainActivity extends Activity { private ProgressDialog pd; private int downloadProgress = 0; private static final String TAG = "AsyncTaskDemo"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pd = new ProgressDialog(this); pd.setTitle(TAG); // 开启一个后台下载任务并执行 new DownloadTask().execute(); } /** * 继承抽象类AsyncTask的一个下载示例类 * AsyncTask<Params, Progress, Result> * Params:是执行后台任务所需要的参数,此处设置为空 * Progress:如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位,此处为整型 * Boolean:任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型,此处设置为布尔型 * */ class DownloadTask extends AsyncTask<Void, Integer, Boolean> { /* * 做一些界面上的初始化操作,例如初始化view * (non-Javadoc) * @see android.os.AsyncTask#onPreExecute() */ @Override protected void onPreExecute() { super.onPreExecute(); pd.show(); } /* * 执行任务的逻辑代码,该方法中的所有代码都会在子线程中运行,处理所有的耗时任务 * (non-Javadoc) * @see android.os.AsyncTask#doInBackground(Params[]) */ @Override protected Boolean doInBackground(Void... params) { try { while (true) { int progress = doDownload(); publishProgress(progress); // 从任务线程切换到UI线程更新执行进度 if (progress >= 100) break; } } catch (Exception e) { Log.e(TAG, "doInBackground Exception"); return false; } return true; } /* * 在这个方法中可以对UI进行操作,更新任务执行进度 * (non-Javadoc) * @see android.os.AsyncTask#onProgressUpdate(Progress[]) */ @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); pd.setMessage("当前下载进度:" + values[0] + "%"); } /* * 显示任务执行结果:成功或者失败 * (non-Javadoc) * @see android.os.AsyncTask#onPostExecute(java.lang.Object) */ @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); pd.dismiss(); if (result) { Toast.makeText(getApplicationContext(), "下载成功", Toast.LENGTH_LONG).show(); } else { Toast.makeText(getApplicationContext(), "下载失败", Toast.LENGTH_LONG).show(); } } /* * 模拟下载功能的函数进度加1,睡眠0.05秒 */ private int doDownload() { downloadProgress++; try { Thread.sleep(50); } catch (InterruptedException e) { Log.e(TAG, "doDownload Thread.sleep(1000) Exception"); e.printStackTrace(); } return downloadProgress; } } }
AsyncTask源码分析
源码分析涉及到的文件和类
- 文件:
AsyncTask.class
FutureTask.class
- 类以及变量:
AsyncTask
FutureTask<V> implements RunnableFuture<V> mFuture
- 其中用到
AsyncTask
的内部类:WorkerRunnable<Params, Result> implements Callable<Result> mWorker
SerialExecutor implements Executor sDefaultExecutor
InternalHandler extends Handler
执行流程
先调用构造函数构造一个异步任务
构造函数中会初始化两个变量,一个是mWorker
一个是mFuture
,两个变量的类型如上分析。
接着执行execute()方法,execute方法会调用executeOnExecutor()方法。
executeOnExecutor()完成两个工作:
(1)执行onPreExecute()
(2)执行exec.execute(mFuture)。
其中doInbackground()和onProgressUpdate()都是在(2)的执行过程中执行的。具体分析见源码解释
最后在后台任务执行完成之后,执行return 返回之后会调用onPostExecute(Result)方法,该方法会发送异步消息更新UI
按执行流程进行的源码分析
类型原型: public abstract class AsyncTask<Params, Progress, Result>
构造函数:
// 初始化了两个变量,mWorker和mFuture,并在初始化mFuture的时候将mWorker作为参数传入 // mWorker是一个Callable对象,mFuture是一个FutureTask对象 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); } } }; }
mWorke
r的类型,该类型实现了public interface Callable<V> {}
接口,在初始化mWorker
的时候重载了该接口中唯一的call()方法
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
在初始化mFuture
的时候,重载了 FutureTask
类的protected void done() { }
方法
构造之后,会调用其execute()方法,该方法的源码如下
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
该方法仅仅执行了AsyncTask类的executeOnExecutor()
方法,该方法的源码如下:
@MainThread 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
是前一段代码中executeOnExecutor()
方法的第一个参数sDefaultExecutor
(在源码的210行,4.3的源码),该参数类型为SerialExecutor
;exec.execute(mFuture)
的参数是mFuture
,mFuture
是一个FutureTask<Result>
类型的变量。下面分别分析代码exec.execute(mFuture)
的调用者和参数的源代码
调用者exec
是SerialExecutor
类型变量,该类型是AsyncTask
的一个内部类,源码如下:
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); } } }
参数mFuture
类型FutureTask<Result>
的run方法如下:
public void run() { if (state != NEW || !U.compareAndSwapObject(this, RUNNER, 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); } }
该run
方法又会调用一个call()
方法,而调用的call()
的是mWorker
,mWorker
是在FutureTask
对象mFuture
初始化的时候传递进去的(见AsyncTask的构造函数)。
mWorker
的类型为实现了Callable
接口的WorkRunnable
内部类
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
FutureTask
类型如下:
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
在AsyncTask
构造函数中,mFuture
的初始化中,mWorker
作为参数构造了一个FutureTask
类型的mFuture
。
构造函数中的mWorker对象初始化代码如下,其中调用了doInBackground()
方法。
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); } };
此时的代码仍然是运行在子线程当中的,所以这也就是为什么我们可以在doInBackground()方法中去处理耗时的逻辑。接着将doInBackground()方法返回的结果传递给了postResult()方法并将其作为Result类型结果返回。
返回结果方法postResult()
的源码如下:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
在postResult()
中便将结果信息以异步消息的形式将消息
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));
发送给Handler
进行处理
更新进度方法publishProgress
源码如下,就是在调用的时候发送一条MESSAGE_POST_PROGRESS
消息:
// 发出更新消息的方法publishProgress() @WorkerThread protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
消息处理类InternalHandler
根据消息类型分别执行finish()
方法或者执行onProgressUpdate()
方法。也就是示例代码中的更新进度或者下载完成关闭进度条的效果。该类是AsyncTask
的内部类,实现源码如下:
// 进行进度更新或者结束更新 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; } } }
涉及的类型和方法比较多,所以叙述起来比较乱,还好大多数都是内部类,基本上也就仅仅是在AsyncTask源码文件中,对照这个流程看看源码注释再跑一跑示例代码就会有更加深入的理解。
- 【Android】AsyncTask原理应用及源码关键部分解析
- Android源码解析AsyncTask的工作原理
- Android 源码解析AsyncTask的工作原理
- android AsyncTask原理解析
- Android AsyncTask原理解析
- Android AsyncTask源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- Android:AsyncTask源码解析
- Android AsyncTask源码解析
- [Android] AsyncTask源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- Android AsyncTask源码解析
- hadoop学习之hadoop完全分布式集群安装
- javascript设计模式实践之策略模式--输入验证
- Spring MVC(三)基于上一篇使用JUnit编写测试用例
- .net 对象浅拷贝和深拷贝的实现
- error LNK2019: 无法解析的外部符号 _Direct3DCreate9@4,该符号在函数 "int __cdecl InitD3D(struct HWND__ *,unsigned lo
- 【Android】AsyncTask原理应用及源码关键部分解析
- 《剑指Offer》读书笔记05:用两个栈实现队列
- 【Struts2】(3)参数传递
- linux 查看库依赖关系
- MySQL分片
- 如何保证网页的字体在各平台都尽量显示为最高质量的黑体?
- 多个进程间共享动态链接库的原理
- linux 查看库依赖关系
- js中变量和jsp中java代码中变量互相访问解决方案