是什么东西?
顾名思义,异步任务,就是说我们可以让我们异步执行任务,不过通常使用它是为了异步执行,主线程更新UI,我们都知道,Android 的UI更新操作,都会检查是否是主线程,如果不是的话就会报出异常,这一步是在ViewRootImpl里面做的,这里就不多说了。在使用的时候,我们只需要将后台处理的代码写在doInBackground,更新UI的工作写在onPostExecute就行了,就可以轻松实现一个线程切换,更新UI的效果。那这里你可能会问。为什么不用Handler,我也许会告诉你,使用 AsyncTask 有很多好处,待会会慢慢告诉你,你也可以根据源码,一探究竟。
前世今生
没有继承自任何东西,Object 除外,使用了模板定义,Params 代表你要传的参数类型,Progress 代表更新数据类型,Result 是任务执行玩返回数据类型。
1 public abstract class AsyncTask<Params, Progress, Result> {}
使用场景
只要需要开启子线程的都可以使用它。在项目中,随便 new Thread 并不是一个好习惯,滥用不说,很有可能造成内存泄露
实现原理大白话
有两个东西很关键,一个是线程池,一个是 Handler,线程池负责执行任务, Handler 负责通知,说道这,聪明的你大概猜到了实现方式,但这并不够,我们需要细细的品味这个类是如何写成的。
源码解析
属性
通过使用 Runtime 对象获取可用的cpu 个数,在创建线程池时创建合适的线程数量,线程池的参数主要是核心线程数,这里最小值2, 最大值4,当处理器个数为4时,创建3个核心线程数,最大线程数为 cpu个数的两倍 + 1,关于核心线程数,最大线程数,大家可自行去了解作用,以后创建线程池,大家可要记住了,以这样创建线程池数是比较合理的。KeepAliveTime 是线程存活时间,超过这个时间的线程要被回收,至于哪一些线程,待会再说
1 2 3 4 5 6 7 private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); // We want at least 2 threads and at most 4 threads in the core pool, // preferring to have 1 less than the CPU count to avoid saturating // the CPU with background work private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE_SECONDS = 30;
静态线程工厂,在类加载是就会被创建,里面使用了AtomicInteger 用来计数。使用了LinkedBlockingQueue作为任务队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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); /** * An {@link Executor} that can be used to execute tasks in parallel. */ public static final Executor THREAD_POOL_EXECUTOR;
使用静态代码块初始化线程池,参数为以上分析的数据,注意,allowCoreThreadTimeOut 设置为true,意思是允许核心线程数被回收,默认是不会被回收的
1 2 3 4 5 6 7 static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; }
还有一个重头戏,单任务执行器,默认不是用上面的线程池执行任务,而是使用序列执行器执行任务,意识就是,同一时刻只能执行一个任务,注意这些都是静态的,也就是说,一个进程,不管你新建了多少个 Task对象,他也只能单个任务执行,这样就很好的管理了资源,那他是如何做到单任务执行的呢,实现方式可以说是既巧妙又无语,且看注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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) { // 新任务进来,不执行,而是使用匿名内部类创建一个Task加到任务队列里, // 当这个执行完的时候,才会去调用下一个任务,就做到了单任务执行。 mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); // 默认先 if (mActive == null) { scheduleNext(); } } // 这里是真正去执行的 // 注意,使用到了 synchronized 的可重入特性! protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }
一些静态的变量基本看完了,下面来看一下构造函数,有点长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 /** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. * * @hide */ public AsyncTask(@Nullable Looper callbackLooper) { // 首先获取 Handler 对象,这里有主线程 Handler 和子线程Handler 之分 mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); // 异步执行的,实现了callable接口 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 // 在这里执行doInBackground函数, // 这是我们需要重写的 result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { // 执行完,这里还是在子线程 postResult(result); } return result; } }; // FutureTask 对象,封装Callable 对象 mFuture = new FutureTask<Result>(mWorker) { // 结束时调用 @Override protected void done() { try { // 如果没有被调用的话,通过get获取结果 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); } } }; }
执行完以后,使用Handler 进行通知,注意,这里将结果装入Message对象中,直接发送。一个小技巧,我们在发送消息是尽可能使用 obtainMessage 获取Message对象要比new一个好得多。
1 2 3 4 5 6 7 8 // 返回结果,使用handler private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
那执行入口呢? MainThread 代表主线程执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } @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; }
一些我们可能需要实现的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 我们将后台逻辑写在这里 @WorkerThread protected abstract Result doInBackground(Params... params); @MainThread protected void onPreExecute() { } // 执行完回调 @MainThread protected void onPostExecute(Result result) { } // 如果有进度条更新 @MainThread protected void onProgressUpdate(Progress... values) { } @MainThread protected void onCancelled() { }
如何更新进度条?只需要在 doInBackground 中调用即可,将参数传入,使用handler机制
1 2 3 4 5 6 7 @WorkerThread protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
Handler 处理消息的地方,静态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private static class InternalHandler extends Handler { public InternalHandler(Looper looper) { super(looper); } @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; } } }
当然,你也可以取消任务,不过只能取消还没执行的任务,底层是调用 thread.interrupt 方法,简单粗暴,关于他的使用,可以写一堆东西来说,这里暂且不展开说
1 2 3 4 public final boolean cancel(boolean mayInterruptIfRunning) { mCancelled.set(true); return mFuture.cancel(mayInterruptIfRunning); }
当然,如果不要于UI交互,你也可以使用这个执行后台逻辑,直接调用executeOnExecutor,使用静态的execute提交,这里是单任务的
1 2 3 public static void execute(Runnable runnable) { sDefaultExecutor.execute(runnable); }
当然,也可以多任务一起执行,调用executeOnExecutor时指定内部的线程池即可AsyncTask.THREAD_POOL_EXECUTOR
总结
我们了解这AsyncTask这个类,大致知道了他是怎么实现了,如果要进一步了解的话,线程池,锁这些东西必不可少的~
如有错误,欢迎讨论