Android之AsyncTask的学习笔记
来源:互联网 发布:唐山学院网络教育 编辑:程序博客网 时间:2024/04/29 02:45
AsyncTask是轻量级的异步任务类,它可以在线程池中执行任务,然后把执行任务的进度和最终结果返回给主线程进行UI更新。从组成结构来讲,AsyncTask用到了Thread和Handler,通过AsyncTask可以更好的执行异步任务以及在主线程中访问UI。但是AsyncTask并不适合执行特别耗时的操作,对于特别耗时的操作,建议使用线程池。
AsyncTask是一个抽象泛型类,其提供了params,progress,result三种泛型参数,其中params是指执行任务的参数的类型,progress是指异步任务返回进度的类型,result是指异步任务返回结果的类型,如果确实不需要这三个泛型参数,可以使用void来代替。
public abstract void AsyncTask<params,progress,result>
1.onPreExecute,这个方法是在主线程中执行,在异步任务开始前被调用,一般用于做一些准备的工作。
2.doInBackground 这个方法在线程池中执行,用于执行异步任务,params参数表示执行任务需要的参数,可以通过publishProgress方法返回当前执行任务的进度,publishProress会调用 onProgressUpdate方法。而且这个方法返回的结果类型会传递给onPostExecute。
3.onProgressUpdate,这个方法在主线程中执行,用于返回当前异步任务的进度。
4.onPostExecute 这个方法在主线程中执行,当异步任务结束后调用该方法,用于返回任务执行的结果。
在上面的方法中,首先执行onPreExecute,接下来是doInBackground,最后是onPostExecute,AsyncTask还提供了onCancelled方法,当异步任务被取消时,onCancelled方法会被调用,但是onPostExecute不会被调用。下面提供一个典型的用法,如下所示。
public class DownLoadFilesTask extends AsyncTask<URL,Integer,Long>{ @Override protected void onPreExecute() { super.onPreExecute(); } @Override 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)); if (isCancelled()){ break; } } return totalSize; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); setProgressPercent(values[0]); } @Override protected void onPostExecute(Long result) { super.onPostExecute(result); showDialog("Downloaded"+ result +"bytes"); }}
在上面的代码中,实现了一个具体的AsyncTask类,这个类主要是模拟异步任务去下载文件的一个过程,它的输入参数类型是URL,后台任务的进度参数为Integer,而后台返回的结果为Long类型。可以看到doInbackground和onProgressUpdate方法中它们的参数类型均包含…的字样,在Java中…表示数量不定,它是一种数组型参数,…的概念和C语言的…是一致的。当要执行上述下载任务时,可以通过以下方式去调用。
new DownLoadFilesTask().execute(url1,url2,url3);
在DownloadFilesTask中,doInBackground用来执行具体的下载任务并通过publishProgress方法来进行更新进度,同时还要判断是否被取消了。当下载任务结束后,doInBackground会返回结果,即下载的总字节数。需要注意的是,doInBackground是在子线程中进行的。onProgressUpdate用于更新任务下载的进度,它运行在主线程中,当publishProgress被调用时,此方法也会被调用。当下载任务完成后,onPostExecute方法也会被调用,它也是运行在主线程中,这个时候我们就可以在界面上进行一个提示的操作,告知用户已经下载成功。
AsyncTask在具体的使用过程也是有一些限制的,主要有如下几点:
1.AsyncTask的类比较在主线程中加载,这就意味着第一次开启AsyncTask的时候必须在主线程。不过在Android4.1及以上版本中已经被系统自动完成。在Android5.0源码中,可以查看ActivityThread中的main方法,它会调用AsyncTask的init方法,这就是满足了AsyncTask的类比较在主线程中加载的条件。
2.AsyncTask的对象必须在主线程中创建。
3.execute方法必须在UI线程中调用。
4.不要在程序中直接调用 onPreExecute,doInBackground,onProgressUpdate,onPostExecute方法。
5.一个AsyncTask只能被执行一次,即execute只能被调用一次,否则会报运行时异常。
6.在Android1.6之前,AsyncTask是串行执行任务的,在Android1.6的时候,AsyncTask开始采用线程池来处理并行任务,但是在Android3.0开始,AsyncTask为了避免因为并行任务造成的错误,AsyncTask又采用了一个线程执行串行任务,尽管如此,在Android3.0及以上版本中,我们仍然可以通过调用executeOnExecutor方法来并行的处理任务。
AsyncTask的工作原理
为了分析AsyncTask的工作原理,我们从它的execute方法开始分析,execute方法又会调用executeOnExecutor方法,如下图所示:
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; }
在上面的代码中,sDefaultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行,这个排队执行的过程后面会进行分析。在executeOnExecutor方法中,AsyncTask的onPreExecute方法最先执行,然后线程池开始执行。下面分析线程池中的执行过程,如下所示:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;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的实现可以分析AsyncTask的排队执行过程。首先系统会把AsyncTask的Params参数封装为FutureTask对象,FutureTask是一个并发类,在这里充电了Runnable的作用。接着这个FutureTask会交给SerialExecutor的execute方法去处理,SerialExecutor的execute方法首先会把FutureTask对象插入到任务队列mTasks中,如果这个时候没有活动的AsyncTask任务,那么就会调用SerialExecutor的scheduleNext方法执行下一个AsyncTask任务。同时当一个AsyncTask任务执行完成后,AsyncTask会继续执行其他任务直到所有的任务都被执行为止,从这一点开始,在默认情况下,AsyncTask是串行执行任务的。
AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正执行任务,InternalHandler用于将执行环境从线程池切换到主线程。在AsyncTask的构造方法中有如下这么一段代码,由于FutureTask的run方法会调用mWorker的call方法,因此mWorker的call方法最终会在线程池中执行。
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return result; } };
在mWorker的call方法中,首先将mTaskInvoked设为true,表示当前任务已经被调用了,然后执行AsyncTask的doInBackground方法,接着讲其返回值传递给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方法会通过sHandler发送一个MESSAGE_POST_RESULT的消息,这个sHandler的定义如下所示:
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; } } }
可以发现,sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程中加载,否则在同一个进程中AsyncTask都将无法正常工作。sHandler收到MESSAGE_POST_RESULT这个消息后会调用AsyncTask的finish方法,如下所示:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
AsyncTask的finish方法的逻辑比较简单,如果AsyncTask被取消执行了,那么就调用onCancelled方法,否则就会调用onPostExecute方法,可以看到doInBackground的返回结果传递给onPostExecute方法,到这里SyncTask的整个工作过程就分析完毕了。
ps:本文摘自《Android开发艺术探索》
- Android之AsyncTask的学习笔记
- Android学习笔记之AsyncTask
- Android应用开发学习笔记之AsyncTask
- Android学习笔记---AsyncTask
- Android AsyncTask学习笔记
- Android 笔记之 AsyncTask
- Android学习笔记1-AsyncTask的用法
- Android学习之 AsyncTask
- Android学习之 AsyncTask
- Android 之AsyncTask 学习
- Android之AsyncTask学习
- Android之AsyncTask学习
- Android学习笔记:Android异步任务之AsyncTask基础
- Android学习笔记之AsyncTask实现文件下载任务
- Android开发学习笔记十四 异步线程之AsyncTask
- android 学习笔记 使用AsyncTask
- [Android] AsyncTask 的学习
- Android入门笔记之AsyncTask
- 第一个照着书写了一段代码,但就是两段代码无法使用,成独立的了:(
- svg移动端粗糙手指划过痕迹
- ZOJ-3715 Kindergarten Election(贪心+枚举)
- SDRAM
- wangzhao 筛选求解素数
- Android之AsyncTask的学习笔记
- 高德地图根据地名获取经纬度
- 二叉树(2)
- KNN-用于回归的python实现
- 关于linuxcast网站课程中MYSQL安装配置基础课程的个人笔记
- php中include、include_once、require、require_once等函数的异同
- 代理模式及具体实现
- linux中find命令和权限粘滞位解读
- 微信小程序开发指南