Android中AsyncTask的使用和源码分析
2017-03-16 10:08
302 查看
前言: 在上一篇Android中的线程和线程池的分析中我们可以看到,异步任务的线程池的一些配置.
连接地址:Android中的线程和线程池一.在Android中AsyncTask的使用
首先: AsyncTask类是一个轻量级的异步任务类,他可以在线程池中执行后台任务;将执行的进度和任务传递到主线程中来更新UI.缺点: 是一个轻量级的异步任务类,并不适合与特别耗时的后台操作.
AsyncTask使用的注意事项:
AsyncTask类必须在主线程中加载,也就是第一次访问AsyncTask必须发生在主线程中,在Android4.1版本以上,主线程的main方法中调用了AsyncTask的init()方法,也就是替我们在主线程中加载了异步任务类.这一点不需要注意.
我们在主线程中创建异步任务的对象
异步任务的execute()方法必须在UI线程中调用
一个异步任务对象只能执行一次execute()方法,执行多次会报运行时错误.
不要直接调用异步任务中的方法,例如 onPreExecute()等方法
我们也可以使用 executeOnExecute()方法来并行的执行任务,默认异步任务类是串行执行任务的.
AsyncTask的几个参数:
由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:
1. Params
在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
2. Progress
后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
3. Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
AsyncTask的几个方法:
1. onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
2. doInBackground(Params...)
这 个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。
任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果 AsyncTask的第三个泛型参数指定的是Void
,就可以不返回任务执行结果。
注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比 如说反馈当前任务的执行进度,可以调用
publishProgress(Progress...)方法来完成。
3. onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在
后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4. onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法
中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
我们根据注意事项来写一个异步任务使用:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 创建异步任务对象 DownFileTask downFileTask = null; downFileTask = new DownFileTask(); //启动异步任务 downFileTask.execute(); } // 三个参数: // 第一个参数是指: doInBackground执行时候的输入参数 // 第二个参数; 异步任务在执行的时候的进度的类型 //第三个参数: 是结束后返回给 onPostExectue()的结果 private class DownFileTask 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]); // 在doInBackgroun中会通过 publishProcess()来调用onProgressUpdate()方法 publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } //运行在主线程中,用于展示异步任务的进度;比如下载的进度 @Override protected void onProgressUpdate(Integer... values) { //在主线程中代用,来更新进度 super.onProgressUpdate(values); } //在异步任务完成之后会被调用,主要是返回的结果 // 在主线程中调用 @Override protected void onPostExecute(Long aLong) { super.onPostExecute(aLong); } } }
二 .根据执行过程进行源码分析:
首先是异步任务的execute()来启动的,我们开看看源码:便于看的明白,明白几个参数的类型:
第一个用于任务排队的一个线程池
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();第二个是sDefaultExecutor指的就是这个线程池(串行线程池)
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
具体的开启的方法:
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }跟进:
在看几个参数的类型:
private final FutureTask<Result> mFuture;
首先FutureTask是一个并发类,最后追查到,这个类继承了Runable和Future两个接口,用来将传递进来的参数params封装为FutureTask对象,之后见mFuture交给SerialExector的exectue()处理.
@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()完成异步任务的一些准备工作.
再看一下子串行线程池源码:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static final int MESSAGE_POST_RESULT = 0x1; private static final int MESSAGE_POST_PROGRESS = 0x2; private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; private static InternalHandler sHandler; private final WorkerRunnable<Params, Result> mWorker; private final FutureTask<Result> mFuture; private volatile Status mStatus = Status.PENDING; private final AtomicBoolean mCancelled = new AtomicBoolean(); private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); private static class SerialExecutor implements Executor { // 任务队列 mTask中会 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(); } } }); // 判断有没有正在活动的异步任务,没有会调用scheduleNext() if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }将FutureTask插入到任务队列中,mTask,接着判断有没有活动的异步任务,没有调用schueduleNext(),也就是说异步任务执行完毕后,才会继续执行其他任务,也就是为什么说异步任务是串行执行的.
上面的线程池只是对异步任务进行排队,真正处理任务的线程池是 THREAD_POOL_EXECUTOR中执行的,我们来看看这个线程池的源码:
/** * An {@link Executor} that can be used to execute tasks in parallel. */ public static final Executor THREAD_POOL_EXECUTOR; 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; }
我们可以看出来,在上一篇中都有很详细的说明,核心线程数是cpu数量+1;非核心线程是cpu核心线程数的2倍+1;
核心线程没有超时策略,非核心线程有超时策略.
/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */ public AsyncTask() { 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; } }; 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); } } }; }
mWorker实际上是一个Callable对象,会调用他的call()方法;在望下面看.
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
private final WorkerRunnable<Params, Result> mWorker;
我们突然看到了一个熟悉的方法,就是后台任务doInBackground()方法,并且获取了结果result,并将这个结果作为postResult()的参数传递,并且设置一个参数的为true就是mTaskInvoked,表示的就是当前的线程已经调用过了!
mTaskInvoked.set(true);
我们接着postResult(参数结果后台任务的)
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
看到了什么?对的就是一个getHandler(),并且使用它发送了一个消息,再看看这个消息就是message_post_result这个方法;
继续看看,这个Handler,异步任务是封装了两线程池和一个Handler,我们需要看看这个Handler;
private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } }
看到一个InternalHnadler对象.
private static class InternalHandler extends Handler { public InternalHandler() { super(Looper.getMainLooper()); }
这个就是我们所谓的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,也就是说 handlerMessage()是在主线程中执行的,我们在细看这个Handler这个类,我们看到是一个static 修饰是一个静态的变量,sHandler是static final修饰会在加载类的时候初始化.结果会发送到主线程中,再次做了一个判断; 如果是异步执行的进度等对ui的更新,会调用
onProgressUpdate()方法
如果是message_post_result的话,会执行finish()方法
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
如果是取消(isCancelled)会执行onCancelled(),附则执行onPostExecute(),状态设置为完成状态.
注意点: mWorker的call()会调用异步任务的后台任务的方法,之后获取任务的返回值,作为参数传递给postResult(),在这个方法里面使用sHandler发送了一条消息,也就是发送到InternalHnadler的handlerMessage()中处理这个消息,完成了线程池切换到主线程的操作.
相关文章推荐
- Android实现异步任务机制AsyncTask 的使用及源码分析
- Android中AsyncTask的使用与源码分析
- Android中AsyncTask的使用与源码分析+3.0以前的缺陷(并发->逐一)
- Android中从源码分析关于AsyncTask的使用
- Android AsyncTask使用步骤与源码分析
- android源码解析(5)--AsyncTask源码分析以及使用中的坑
- Android中从源码分析关于AsyncTask的使用
- Android中从源码分析关于AsyncTask的使用
- Android中AsyncTask的使用与源码分析
- Android进阶——多线程系列之异步任务AsyncTask的使用与源码分析
- Android中AsyncTask使用教程及源码分析
- Android中AsyncTask的使用与源码分析
- Android中AsyncTask的使用与源码分析
- Android中从源码分析关于AsyncTask的使用
- Android源码分析之AsyncTask
- 天天记录 - Android 使用dmeo和源码分析invalidate流程
- Android源码分析—带你认识不一样的AsyncTask
- Android AsyncTask运作原理和源码分析
- Android源码分析—带你认识不一样的AsyncTask(串并行)
- 源码分析Android AsyncTask