Android AsyncTask详解
2015-12-28 09:26
441 查看
前言
一提到多线程,我们不得不提到AsyncTask,很多Android开发人员在网络请求这块,一般会使用开源框架,比如Volley,Okhttp,retrofit等。但是有一部分人,比较忠于封装AsyncTask去实现。我们都知道AsyncTask内部是Handler实现的,今天我们就来一探究竟。基本使用
AsyncTask使用了模板方法模式,使用AsyncTask,我们需要写一个类去继承它,然后重写它的几个方法,如下:public class MyAsyncTask extends AsyncTask<String, Integer, String> { @Override protected void onPreExecute() { // TODO Auto-generated method stub super.onPreExecute(); } @Override protected String doInBackground(String... params) { // TODO Auto-generated method stub return null; } @Override protected void onProgressUpdate(Integer... values) { // TODO Auto-generated method stub super.onProgressUpdate(values); } @Override protected void onPostExecute(String result) { // TODO Auto-generated method stub super.onPostExecute(result); } }
AsyncTask传入的参数分别代表:在调用excute方法传入的参数类型,onProgressUpdate方法的参数类型和doInBackground返回的参数类型。下面再说明下几个方法:
a.onPreExecute:最先执行的方法,运行在主线程,一般我们做显示Dialog等的准备工作。
b.doInBackground:在onPreExecute只后执行的方法,运行在线程池,这里主要做耗时操作,然后将结果返回。
c.onPostExecute:最后执行的方法,运行在主线程,方法参数为doInBackground返回的值。
d.onProgressUpdate:显示请求任务进度的方法,运行在主线程,需要在doInBackground调用publishProgress(values)方法。比如说下载任务进度。。。
在使用的时候就直接:
new MyAsyncTask().excute();//当然也可以传入一些参数
基本使用就差不多介绍完毕,下面我们罗列下AsyncTask需要注意的地方:
AsyncTask必须在主线程中加载。不过在android4.1以上系统自动完成了。AsyncTask必须在主线程中创建。excute方法必须在主线程中调用。
一个AsyncTask对象只能执行一次,也就是只能调用一次excute方法,否则会报异常。
Android1.6之前AsyncTask是串行执行任务。Android1.6到Android3.0是并行执行任务。Android3.0以后是串行执行任务,不过我们可以通过自定义线程池,调用executeOnExecutor()方法,将自定义的线程传入,然后并行处理。
不要显示去调用onPreExecute等方法。
源码分析
基本使用介绍完毕后,我们来看下AsyncTask的内部实现,先从我们的调用方法excute开始:public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
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; }
这边分析的源码是android4.4,默认传入线程池是串行去处理的。代码3行开始,我们可以看到这里有个状态判断。总共有3个状态:Status.PENDING,Status.RUNNING,Status.FINISHED;执行前,执行中,执行完。代码17行我们就能看到onPreExecute()方法是最先执行的。20行exec.execute(mFuture);,线程池开始工作:
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 和THREAD_POOL_EXECUTOR。从代码中我们可以看出:SerialExecutor主要是做任务的排队,而THREAD_POOL_EXECUTOR才是真正的做后台工作。
在AsyncTask中就初始化了两个对象mWorker和mFuture,这个在executeOnExecutor方法里就应用到。而上述代码第9行,调用mFuture.run方法,就保证了mWorker在线程池中调用call方法:
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } };
进入postResult方法:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
这里就用到异步消息机制,进入handler:
private static class InternalHandler extends Handler { @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; } } }
我们可以看到InternalHandler 是一个静态Handler对象,为了能够将环境最终切换到主线程,那么InternalHandler就必须在主线程中。由于静态成员会在加载类的时候初始化,那么就变相的要求AsyncTask必须在主线程中加载了。这也印证了我们在前面提出的观点。
然后,这里对消息的类型进行了判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。进入finish():
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
可以看到,如果当前任务被取消掉了,就会调用onCancelled()方法,如果没有被取消,则调用onPostExecute()方法,这样当前任务的执行就全部结束了。
上述有一个消息MESSAGE_POST_PROGRESS处理,然后调用onProgressUpdate,发消息的地方,我们应该能想到是:publishProgress方法,如下:
protected final void publishProgress(Progress... values) { if (!isCancelled()) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
至此,我们相信我们对于AsyncTask的工作原理及注意事项已经理解的很透彻了。
相关文章推荐
- Python3写爬虫(四)多线程实现数据爬取
- C#实现多线程的同步方法实例分析
- 浅谈chuck-lua中的多线程
- C#简单多线程同步和优先权用法实例
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- C#多线程编程中的锁系统(三)
- C#多线程学习之(六)互斥对象用法实例
- 基于一个应用程序多线程误用的分析详解
- C#多线程学习之(三)生产者和消费者用法分析
- C#多线程学习之(一)多线程的相关概念分析
- C#多线程之Thread中Thread.IsAlive属性用法分析
- 分享我在工作中遇到的多线程下导致RCW无法释放的问题
- C#多线程编程之使用ReaderWriterLock类实现多用户读与单用户写同步的方法
- C#控制台下测试多线程的方法
- Ruby 多线程的潜力和弱点分析
- c++线程池实现方法
- c++实现简单的线程池
- C#中WPF使用多线程调用窗体组件的方法
- C#如何对多线程、多任务管理(demo)