Android 并发二三事之AsyncTask
来源:互联网 发布:投资组合优化模型 编辑:程序博客网 时间:2024/06/05 11:18
Android 并发第四篇
前言:
本篇主要详解AsyncTask 的源码,关于 AsyncTask 的源码其实有太多人都写过了。这里为什么还要写,
是因为博主在并发系列中写AsyncTask的源码,是想通过从并发的角度去理解AsyncTask 为什么这样设计。
我们可以看到 AsyncTask 其中用到了 之前文章中设计到的 FutureTask, 以及Callable,线程池等等。
我们也可以借此复习一下。
一 AsyncTask 的使用:
代码:
private void requestAsy() { AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() { @Override protected String doInBackground(String... params) { List<ResponInfo> responInfos = getInfos(); long price = 0; for(ResponInfo responInfo : responInfos) { if(responInfo.getName().equals(params[0])) { price = responInfo.getPrice(); } } return String.valueOf(price); } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected void onPostExecute(String s) { super.onPostExecute(s); Log.d(TAG,"Asy : "+s); } }; //无参 asyncTask.execute(); //或 有参 asyncTask.execute("BMW"); //或 传入我们自定义的线程池 asyncTask.executeOnExecutor(Executors.newFixedThreadPool(10)); } private List<ResponInfo> getInfos() { List<ResponInfo> list = new ArrayList<>(); list.add(new ResponInfo("BMW", 2000)); list.add(new ResponInfo("BYD", 200)); try { Thread.sleep(3000); }catch (Exception e){ } return list; }
二 源码:
1、 execute() 方法:
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
execute 调用的也是 executeOnExecutor()方法,只不过传入的是代码内的默认线程池 sDefaultExecutor。
sDefaultExecutor 这个参数我们稍后再说,现在只要记得它是一个线程池就好了,可以传入 Runnable,或者 Callable。
2、 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; }
当我们调用executeOnExecutor() 方法,将不会用AsyncTask 自己的线程池,而改用我们所传入的线程池。
在源码中还有这样的方法:
/** @hide */ public static void setDefaultExecutor(Executor exec) { sDefaultExecutor = exec; }
设置默认的线程池。
不过在最新的版本中被隐藏了,因为有executeOnExecutor() 方法,就用不上这个方法了。
在 executeOnExecutor() 方法中我们看到其调用了onPreExecute() 方法,所以当我们重写 onPreExecute() 方法后, onPreExecute() 会执行在主
线程之中。
我们会将传入的参数传给 mWorker.mParams 。
然后调用线程池执行 mFuture。mFuture 是 FutureTask 根据第一篇文章中的介绍 mFuture 既是 Runnable, 又是 Future。
所以其可以作为一个任务(Runnable)提交给线程池,也可以作为一个Future,从线程池中获取结果。
3 、看构造方法源码:
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(); //将结果post出来 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); } } }; }
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
mWorker 本身是一个Callable ,mFuture 是 FutureTask 。FutureTask 在第二篇文章中,有过介绍,其实现了RunnableFuture接口,
而RunnableFuture 接口同时继承了Runnable和Future 。所以FutureTask 可以作为一个任务被提交给线程池。这里FutureTask 包装了 mWorker。
mWorker 才是真正的任务主体。
mWorker以及 mFuture都是在构造函数中初始化的。
mWorker 作为任务主体,被提交到线程池中,其call() 方法将会执行在子线程之中。而在call() 方法中,调用了doInBackground()方法
所以doInBackground()方法将会执行在子线程之中。并且将mWorker.mParams 传给 doInBackground()方法,所以doInBackground()方法
的参数就是我们 在调用 execute() 和 executeOnExecutor()传入的参数,其类型便是我们在 new AsyncTask() 时的三个泛型中第一个。
在执行过耗时任务之后。会调用 postResult() 方法将结果post出来。
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); //发送消息 message.sendToTarget(); return result; }
这里getHandler()方法返回的是InternalHandler。
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; } } }
发送了 MESSAGE_POST_RESULT 的消息后,在handleMessage() 中调用了result.mTask.finish(result.mData[0]);
result 是 new AsyncTaskResult(this, result) 。
而AsyncTaskResult 只是封装了真正的数据,以及当前 AsyncTask 对象。
private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }
result.mTask.finish(result.mData[0]);
这行代码实际上调用了 AsyncTask 中的finish() 方法。
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
finish() 方法中则调用了 onPostExecute() 方法。这个流程就是典型的,子线程发送消息通知主线程了,所以onPostExecute() 方法会执行在主线程中。
那么说到这里,整个流程差不多快说完了。但是我们还没有介绍 FutureTask 也就是mFuture 到底是用来做什么,为什么要用他呢?
按照之前的罗逻辑完全可以不用 mFuture 来包装 mWorker 。mWorker 就可以直接被提交给线程池来完成任务。
4 、下面是 FuturenTask 部分源码:
//FuturenTask 包装了一个 Callable。 public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } 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 { //在这里执行了 Callable 的 call() 方法。获取结果。 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); } } protected void set(V v) { if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) { outcome = v; U.putOrderedInt(this, STATE, NORMAL); // final state finishCompletion(); } } private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { if (U.compareAndSwapObject(this, WAITERS, q, null)) { for (;;) { Thread t = q.thread; if (t != null) { q.thread = null; LockSupport.unpark(t); } WaitNode next = q.next; if (next == null) break; q.next = null; // unlink to help gc q = next; } break; } } //最后会调用done()方法。 done(); callable = null; // to reduce footprint }
在 AsyncTask 的源码中重写了done() 方法,其中调用了 postResultIfNotInvoked() 方法。
private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { postResult(result); } }
postResultIfNotInvoked() 也会调用 postResult() 方法,但是其不一定会执行。
因为在mWorker 的 call() 方法中已经设置 为 true 了。
mTaskInvoked.set(true);
所以我在这里推测之所以这么写,是防止出现异常,导致出现没有回调结果的情况。在正常的情况下,postResultIfNotInvoked() 方法是不会
调用 postResult() 方法的。
5 、那么为什么要用 FutureTask 呢? (当然这是我认为,可能有错)
下看 AsyncTask 中的其他几个方法:
public final boolean cancel(boolean mayInterruptIfRunning) { mCancelled.set(true); return mFuture.cancel(mayInterruptIfRunning); } public final Result get() throws InterruptedException, ExecutionException { return mFuture.get(); } public final Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return mFuture.get(timeout, unit); }
当调用这想通过 get() 方法直接获取结果时,可以直接调用 AsyncTask.get() 等待结果的返回。
可以看到 AsyncTask 中的这几个方法,全部都是依托于 FutureTask 实现。当前线程阻塞,等待结果的返回。
这就是我猜测其用 FutureTask 的原因。可以直接获取结果。
6、 下面来看最开始放在一边的默认的线程池 : 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); } } }
可以看到的是 SerialExecutor 只是实现了 Executor 接口,它只是重写了execute() 方法。将Runnable 包装一下,放进了队列之中。
它并不是一个真正的线程池。将Runnable 放入队列中后,调用了 scheduleNext() 方法。
其实在 scheduleNext() 方法中才是真正的调用线程池执行任务。
THREAD_POOL_EXECUTOR:
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
THREAD_POOL_EXECUTOR 才是一个真正的线程池。关于 THREAD_POOL_EXECUTOR 到底是一个什么样的线程池,可以去看第一篇文章,
那里有详细的介绍。
那为什么 AsyncTask 要搞出 SerialExecutor 这么个鬼呢?
仔细研究其逻辑:
第一此调用时,会把Runnable 放到队列中,然后执行 scheduleNext() 方法,在 scheduleNext() 中判断队列是否存在,是否有数值。
有数值则调用线程池执行。如果这个时候又添加了一个任务有是被加到队列中,mActive 不为空,所以不会调用 scheduleNext() 方法执行任务。
所以这个任务会放在队列之中。这时上一个任务执行完了,会再次调用 scheduleNext() 方法,从队列中获取任务,如果有,则执行。
所以废了这么大的劲,其实就是想维护一个每次只能执行一个任务的线程池,所有的任务会依次执行。
当然如何你不想这样,希望每次可以执行很多任务,那就调用 executeOnExecutor() 方法,传入自定义的线程池,或者 THREAD_POOL_EXECUTOR 每次执行
多个任务。
三 总结:
OK , AsyncTask 的源码就分析到这里。其实到最后我们会发现,真正实现并发的其实还是线程池,AsyncTask 只不过是将其封装一下,暴露一些接口以及参数。方便于调用者使用。我觉得更应该值得学习应该是它封装的思想或者说是思路吧,即如何能通过封装,提供更好的使用,满足更多的需求。
四 下一篇
下一篇会介绍 Android 中另一个可以实现异步操作的类:HandlerThread。并且尝试自定义 HandlerThread ,使其用法更加趋于简单。
- 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
- Run Time中请求系统权限Requesting Permissions at Run Time
- 一行代码实现java list去重
- PAT04-树6 Complete Binary Search Tree 【JAVA实现】
- File操作-RandomAccessFile
- CentOS7与MySQL
- Android 并发二三事之AsyncTask
- 《iOS移动开发从入门到精通》图书连载5:Xcode 8的使用(上)
- EditText的部分操作
- 内网一键生成 LetsEncrypt HTTPS证书 - 3 - 生成证书
- C++11语言可用性的强化
- 自定义控件无法直接添加到axml解决方法
- redis的消息队列
- 28. Implement strStr()
- C#——多态