AsyncTask的简单理解(源码级)
2015-11-10 13:54
253 查看
AsyncTask的简单理解
AsyncTask里面有两个线程池ThreadPoolExecutor和
SerialExecutor还有一个
Handler。,其中
SerialExecutor用来给线程排队,真正执行的是
ThreadPoolExecutor,
Handler用于线程之间的切换。给AsyncTask排队的容器是一个单向队列
ArrayDeque<Runnable>,他保存了所有将要执行的线程。先来看一下熟悉的
execute方法
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
他调用了executeOnExecutor方法,同时把用于排队的线程池和在doInBackground需要用到的参数传进去。
@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(),所以说
onPreExecute()在主线程调用。然后他使用了
exec.execute(mFuture);来排队,这里的exec就是上面传过来的
sDefaultExecutor,也就是用于排队的线程池。那么他的参数
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() { ...... } }; }
上面代码中
WorkerRunnable是一个抽象类,他封装了Params[],
WorkerRunnable也实现了
Callable<Result>接口,用于把这条线程放到
FutureTask里面进行执行。
FutureTask会自动调用
WorkerRunnable里面重写的
call()方法,而在这个
call()正有我们熟悉的
doInBackground(mParams);
doInBackground();
如何处理结果
当运行结束后,返回一个result,由于他运行在子线程里面,需要把结果提交到主线程来显示,所以他会调用
postResult(result);来显示结果。线程之间的切换用的是Handler。
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; } } }
上面是Handler如果处理结果。运行到
result.mTask.finish(result.mData[0]);他会执行
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
看到
onPostExecute(result)了吧,他就是用于接受结果的回调方法。由于用到了Handler切换线程,所以他运行在主线程。
如何更新进度
我们都知道在子线程里面更新进度需要调用
publishProgress方法。
@WorkerThread protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
首先在消息池里面获取到
MESSAGE_POST_PROGRESS消息。然后把
AsyncTaskResult<Progress>的实例放到里面。这个
AsyncTaskResult<Progress>是什么呢?
private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }
他是一个静态类,构造器里面封装的AsyncTask和需要被更新的值。
然后
Handler会把它发送到what为
MESSAGE_POST_PROGRESS的消息中,用来更新进度。
线程池是如何工作的。
1.排队线程池,先看代码
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); } } }
他也是一个静态类,实现了
Executor,并重写了execute方法。里面维护这一个
ArrayDeque他是一个单向的队列。这里用到了他的两个方法。
offer()和
poll(),其中
offer()方法是向队列最后面插入一条数据mTasks,
poll()是从队列的最前面取出一条数据并将其删除。
他在插入的同时新建一个
Runable同时执行了传入这条线程的
run()方法。这里你也许要问,为什么一
Runable要套一个
Runable呢,既然是排队那么为什么要运行
run()方法呢?
1、如果这个
Runable如果不包含一个
Runable,那么这个传进来的
Runable就会直接执行的,注意,是执行在主线程。另外一个线程池没有起到作用。
2、这里虽然运行了
run()方法真的线程也没有执行,里面线程虽然调用了
run()方法,仅仅是调用而已,外面的Runable没有执行,里面的
run()方法就不会执行。
排队之后他会执行
scheduleNext();方法,这个方法是启动分线程的关键。他先从队列中取出
Runable,然后调用
ThreadPoolExecutor的
execute()方法来真正执行线程。
线程池的参数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; 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()); } }; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
自己看吧,谷歌写的很明白
相关文章推荐
- C#线程间不能调用剪切板的解决方法
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- C#线程同步的三类情景分析
- C#获取进程或线程相关信息的方法
- C#停止线程的方法
- C#子线程更新UI控件的方法实例总结
- C#线程队列用法实例分析
- C++使用CriticalSection实现线程同步实例
- c++线程池实现方法
- 基于C++实现的线程休眠代码
- c++实现简单的线程池
- VB读取线程、句柄及写入内存的API代码实例
- C#网络编程基础之进程和线程详解
- C#通过Semaphore类控制线程队列的方法
- C#多线程处理多个队列数据的方法
- C#实现线程安全的简易日志记录方法
- C#中线程同步对象的方法分析
- ASP.NET线程相关配置
- 浅析linux环境下一个进程最多能有多少个线程
- 再谈JavaScript线程