AsyncTask可以非常方便、简单的在UI线程中使用,它帮助我们在不使用Thread和Handler的情况下,在后台执行一个后台(异步)任务,并且把执行结果通知到UI线程中。也就是说,使用AsyncTask可以极大的简化我们进行后台任务的操作,使用它,我们不必关心工作线程是如何启动的,也不必关心,工作线程和UI线程之间的通信问题,这些都被AsyncTask通过内部封装实现了,我们只需要按接口规范使用它即可。
AsyncTask适用于短时的后台(异步)任务(最多几秒钟),它并不适合时间较长的后台任务,如果我们想在后台线程执行长时间的异步任务,可以使用java.util.concurrent并发工具包中的Executor、ThreadPoolExecutor和FutureTask等。
AsyncTask执行后台任务,是由后台线程执行具体的任务,最后把结果发布到UI线程。
一个后台任务的声明由3种泛型类型:Params、Progress和Result,以及4个过程:onPreExecute、doInBackground、onProgressUpdate和onPostExecute构成。
Params:doInBackground方法的参数类型,它会在任务执行之前发送给后台任务。
Progress:onProgressUpdate方法的参数类型,它是在任务执行期间发布的进度类型。
Result:onPostExecute方法的参数类型,它是后台任务执行结束后的结果类型。
示例:
private class DownloadFilesTask extends AsyncTask<String, Integer, Long> {}
示例中的Params、Progress和Result分别是String、Integer和Long。
onPreExecute():UI线程中执行。它发生在后台任务开始执行之前,我们可以通过它来进行任务的配置,比如开始展示进度条等。
doInBackground(Params…):后台线程中执行。它在onPreExecute完成之后立即执行,它会执行较长时间的后台运算任务。运算结果必须由该步骤return,发布给onPostExecute方法。我们也可以在该过程中,调用一次或多次publishProgress(Progress…)方法来发布执行进度信息,进度信息最终会发布到UI线程中,成为onProgressUpdate(Progress…)的参数。
onProgressUpdate(Progress…):UI线程中执行。它是在调用publishProgress(Progress…)之后执行的,它通常用来展示后台任务的执行进度。
onPostExecute(Result):UI线程中执行。它是在后台任务执行结束之后调用的,由doInBackground(Params…)方法return的结果作为参数。
AsyncTask后台任务执行中的任意时刻,都可以调用cancel(boolean)来进行取消操作。
示例代码,创建了一个DownloadFilesTask类,继承于AsyncTask,用于执行后台任务。
private class DownloadFilesTask extends AsyncTask<String, Integer, Long> {//3个泛型分别为:String, Integer, Long protected Long doInBackground(String... urls) {//参数为字符串列表 int count = urls.length; long time = 0; for (int i = 0; i < count; i++) { try { Thread.sleep(1000);//示例任务,执行线程休眠1秒,模拟耗时任务 } catch (InterruptedException e) { e.printStackTrace(); } time += 1000; publishProgress((int) (((i + 1) / (float) count) * 100));//更新进度 if (isCancelled()) break;//检测是否执行过程中取消了任务 } return time; } protected void onProgressUpdate(Integer... progress) { //更新进度。这里的参数是Integer对象 mTextView.setText("进度:" + progress[0]); } protected void onPostExecute(Long result) { //执行结果回调 mTextView.setText("time : " + result + " 毫秒"); } }
异步任务准备好了,我们创建一个DownloadFilesTask类的实例,调用execute()方法即可执行:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new DownloadFilesTask().execute("url1", "url2", "url3", "url3"); }
例子很简单,但是也完整的展示了AsyncTask的基本用法。
接下来我们来分析AsyncTask的实现原理。
我们接下来分析AsyncTask的源码,看下它的实现原理。
在Demo中,我们调用AsyncTask的实例对象的execute方法来实现调用的,那么我们就从execute方法作为入口点来分析。
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
这里调用了executeOnExecutor方法,参数sDefaultExecutor是一个线程池,作为默认线程池。
@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; }
线程池执行的部分我们后面来分析,先来看这里有2个重要的属性mWorker和mFuture,它们起到了非常重要的作用,我们来看。
属性mWorker和mFuture是在AsyncTask的构造方法中初始化的,它们是后台线程控制逻辑的核心所在。
我们来看AsyncTask的构造方法:
public AsyncTask() {//这里是在UI线程中执行的 this((Looper) null); } public AsyncTask(@Nullable Handler handler) { this(handler != null ? handler.getLooper() : null); } public AsyncTask(@Nullable Looper callbackLooper) { mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception {//这里是在后台线程中执行的 mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//这里设置了线程的优先级为THREAD_PRIORITY_BACKGROUND //noinspection unchecked result = doInBackground(mParams);//这里调用了doInBackground方法 Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); //post结果给UI线程 } return 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); } } }; } private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
我们来看,后台任务处理完成后,会在WorkerRunnable的call方法中,调用postResult(result)将结果返回给UI线程,但如果后台任务执行之前被终止,则会把结果传递给postResultIfNotInvoked方法。
我们来看这两个方法:
private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { //同步属性的布尔值,表示后台任务是否已经开始执行 postResult(result);//这里,后台任务并没有执行 } } 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对象,把执行结果封装成一个AsyncTaskResult对象,通过Handler机制,将后台执行结果传递给UI线程(这里只考虑UI线程一种情况)。
到了这里,Asynctask的执行部分、结果处理以及跨线程通信部分,都已经分析完成了,接下来我们来看后台线程是如何执行的,任务队列是如何实现的。
我们从上文知道,AsyncTask的execute方法中,直接使用了一个默认线程池sDefaultExecutor来负责后台线程的创建及执行。
回顾一下:
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; public static final Executor SERIAL_EXECUTOR = new 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是SerialExecutor的一个实例对象。
mTasks:是一个先进先出的队列,这里可以理解为容量无限的一个先进先出的队列(当然不可能无限,不过正常情况下我们是达不到它的最大容量的)。
mActive:表示当前正在执行的任务,它是一个Runnable对象。
串行执行的秘密:这里其实就实现了一个单线程的任务队列,所有AnsyncTask的任务,都会顺序的,单线程执行。它的实现原理其实就是,准备了一个可以扩容的先进先出的任务队列mTasks,所有的后台任务执行时,先进入队列中,然后调用scheduleNext执行,当前任务结束后,继续执行下一个任务,这样也就实现了串行的任务处理。
这里的线程池其实没有发挥线程池的作用,因为默认情况下,AsyncTask只会执行单个任务调用,因为在SerialExecutor中已经实现了任务的队列管理了,SerialExecutor会单个顺序的执行线程任务的调用。
public static final Executor THREAD_POOL_EXECUTOR; private static final int CORE_POOL_SIZE = 1; private static final int MAXIMUM_POOL_SIZE = 20; private static final int BACKUP_POOL_SIZE = 5; private static final int KEEP_ALIVE_SECONDS = 3; static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), sThreadFactory); threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy); THREAD_POOL_EXECUTOR = threadPoolExecutor; }
THREAD_POOL_EXECUTOR其实是一个可并发的线程池,它的核心线程数是1,最大线程数是20。(注意,不同Android版本中的实现会有不同)
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()); } };
线程池是通过ThreadFactory对象来进行线程创建的,并且会为线程命名为”AsyncTask #” + mCount.getAndIncrement()。
在Android 3.0时,AsyncTask的任务执行改回了单一线程中顺序执行,那么我们如果想用AsyncTask实现并发任务,可以做到吗?答案是肯定的,我们来看如何实现。
我们来看AsyncTask的executeOnExecutor方法:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { …… exec.execute(mFuture); return this; }
executeOnExecutor方法,可以接受一个线程池的参数,来让使用者实现自定义线程池的目的。
我们可以自定义一个并发的线程池,通过调用executeOnExecutor方法,替换掉AsyncTask的默认线程池,即可实现并发处理。
注意:虽然这里可以实现线程池的并发处理,但是并不建议这么做,如果想要实现并发的后台任务,推荐使用java.util.concurrent并发工具包中的Executor、ThreadPoolExecutor和FutureTask等。
到了这里,我们已经深入了解了AsyncTask的使用及实现原理了,那么我们在开发时,有哪些是需要注意的呢?
AsyncTask后台任务的执行顺序是不可靠的,因为它在Android历史版本中经历了多次变更。在刚刚引入AsyncTask时,AsyncTask后台任务的执行,是在单一线程中顺序执行的;但是在Android 1.6中,AsyncTask的任务执行改成了可多线程并发执行;在Android 3.0时,AsyncTask的任务执行又改回了单一线程中顺序执行,以避免多线程造成的并发等问题。
如果我们想通过AsyncTask来并发执行后台任务,可以使用executeOnExecutor(java.util.concurrent.Executor, java.lang.Object[])方法,传递一个线程池来执行。
注意:AsyncTask在Android R(Android 11)中被标记为弃用状态,建议使用java.util.concurrent并发工具包来代替。
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算