Android AsyncTask源码分析

来源:互联网 发布:金山软件股价 编辑:程序博客网 时间:2024/06/05 02:58

https://segmentfault.com/a/1190000004699080

简介

Android中只能在主线程中进行UI操作,如果是其它子线程,需要借助异步消息处理机制Handler。除此之外,还有个非常方便的AsyncTask类,这个类内部封装了Handler和线程池。本文先简要介绍AsyncTask的用法,然后分析具体实现。

1.1  AsyncTask实例使用

下面是一个使用AsyncTask的实例,通过指定URL利用网络下载资源(此例模拟资源为字符串),以模拟耗时任务。在下载过程中,会通过进度条对话框向用户展示进度。在完成任务后将字符串展示在TextView上。具体实现细节后面会加以讲述,顺便引出AsyncTask的知识。

[java] view plain copy
  1. public class MainActivity extends Activity{  
  2.     private TextView show;  
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState){  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.main);  
  7.         show = (TextView) findViewById(R.id.show);  
  8.     }  
  9.     //按钮事件响应方法  URL可自定义  
  10.     public void download(View source) throws Exception{  
  11.         DownTask task = new DownTask(this);  
  12.         task.execute(new URL(URL));  
  13.     }  
  14.     class DownTask extends AsyncTask<URL, Integer, String>{ //自定义Task类继承AsyncTask  
  15.         ProgressDialog pdialog;  
  16.         int hasRead = 0;  
  17.         Context mContext;  
  18.         public DownTask(Context ctx){  
  19.             mContext = ctx;  
  20.         }  
  21.         @Override  
  22.         protected String doInBackground(URL... params){  //doInBackground方法在子线程执行耗时任务  
  23.             StringBuilder sb = new StringBuilder();  
  24.             try{  
  25.                 URLConnection conn = params[0].openConnection();  
  26.                 BufferedReader br = new BufferedReader(  
  27.                     new InputStreamReader(conn.getInputStream(), "utf-8"));  
  28.                 String line = null;  
  29.                 while ((line = br.readLine()) != null){  
  30.                     sb.append(line + "\n");  
  31.                     hasRead++;  
  32.                     publishProgress(hasRead);  
  33.                 }  
  34.                 return sb.toString();  
  35.             }  
  36.             catch (Exception e){  
  37.                 e.printStackTrace();  
  38.             }  
  39.             return null;  
  40.         }  
  41.         @Override  
  42.         protected void onPostExecute(String result){ //主线程执行  
  43.             // 展示下载下来的字符串 并将进度条对话框dismiss  
  44.             show.setText(result);  
  45.             pdialog.dismiss();  
  46.         }  
  47.         @Override  
  48.         protected void onPreExecute(){ //主线程执行  
  49.             pdialog = new ProgressDialog(mContext);  
  50.             pdialog.setTitle("任务正在执行中");  
  51.             pdialog.setMessage("请等待...");  
  52.             // 设置对话框不能用“取消”按钮关闭  
  53.             pdialog.setCancelable(false);  
  54.             pdialog.setMax(MAX);  
  55.             // 设置对话框的进度条风格  
  56.             pdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
  57.             // 设置对话框的进度条是否显示进度  
  58.             pdialog.setIndeterminate(false);  
  59.             pdialog.show();  
  60.         }  
  61.         @Override  
  62.         protected void onProgressUpdate(Integer... values){ //主线程执行  
  63.             // 更新进度  
  64.             show.setText("已经读取了" + values[0] + "行");  
  65.             pdialog.setProgress(values[0]);  
  66.         }  
  67.     }  
  68. }  



基本用法

AsyncTask是一个抽象类,我们需要创建子类去继承它,并且重写一些方法。AsyncTask接受三个泛型参数:

  • Params: 指定传给任务执行时的参数的类型

  • Progress: 指定后台任务执行时将任务进度返回给UI线程的参数类型

  • Result: 指定任务完成后返回的结果的类型

除了指定泛型参数,还需要根据需要重写一些方法,常用的如下:

  • onPreExecute(): 这个方法在UI线程调用,用于在任务执行前做一些初始化操作,如在界面上显示加载进度控件

  • doInBackground: 在onPreExecute()结束之后立刻在后台线程调用,用于耗时操作。在这个方法中可调用publishProgress方法返回任务的执行进度

  • onProgressUpdate: 在doInBackground调用publishProgress后被调用,工作在UI线程

  • onPostExecute: 后台任务结束后被调用,工作在UI线程

源码分析

下面分析这个类的实现,主要有线程池以及Handler两部分。

线程池

当执行一个AsyncTask的时候调用的是execute()方法,就从这个开始看:

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    onPreExecute();        mWorker.mParams = params;        exec.execute(mFuture);      return this; } 

execute方法会调用executeOnExecutor。在这个方法中先检查任务是否已经执行或者执行结束,然后把任务标记为running。最开始执行的是onPreExecute,接着把参数赋值给mWorker对象。这个mWorker是一个Callable对象,最终被包装为FutureTask

【拓展:FutureTask是什么】

FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等。当然,由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。


代码如下:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {      Params[] mParams;  } 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));          }      };    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 occured while executing doInBackground()",                      e.getCause());          } catch (CancellationException e) {              postResultIfNotInvoked(null);          }      }  };     

从上面的代码可以看出,在mWorker对象中的call()方法会调用doInbackground,返回值交给postResult方法,这个方法通过Handler发送消息,这一点稍后再详细分析。

mWorker对象被封装成FutureTask之后交由线程池执行,从execute方法可以看出,使用的是sDefaultExecutor,它的值默认为SERIAL_EXECUTOR,也就是串行执行器,实现如下:

 private static class SerialExecutor implements Executor {      //线性双向队列,用来存储所有的AsyncTask任务      final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();      //当前正在执行的AsyncTask任务      Runnable mActive;      public synchronized void execute(final Runnable r) {          //将新的AsyncTask任务加入到双向队列中          mTasks.offer(new Runnable() {              public void run() {                  try {                      //执行AsyncTask任务                      r.run();                  } finally {                      //当前任务执行结束后执行下一个任务                    scheduleNext();                  }              }          });            if (mActive == null) {              scheduleNext();          }      }      protected synchronized void scheduleNext() {          //从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行          if ((mActive = mTasks.poll()) != null) {              THREAD_POOL_EXECUTOR.execute(mActive);          }      }  }public static final Executor THREAD_POOL_EXECUTOR          = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,                  TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);    

execute()方法中首先将FurtherTask对象(充当了Runnable的作用)插入到任务队列mTasks的尾部。新建的Runnable对象判空后进入scheduleNext方法。然后通过mTasks.poll()被取出,然后交给一个叫THREAD_POOL_EXECUTOR的线程池去执行(前面的SerialExecutor用于任务的排队)


在上面的代码中,如果有任务执行,那么SerialExecutorexecute方法会被调用,它的逻辑是把Runnable对象加入ArrayDeque队列中,然后判断mActivie是否为空。第一次执行时mActive当然为空,所以执行scheduleNext,其实就是取出任务队列中的第一个任务交给线程池(THREAD_POOL_EXECUTOR)执行。加入mTask队列的Runnable对象的run方法里最终一定会调用scheduleNext,那么又会从任务队列中取出队头任务执行。这样便实现了单线程顺序执行任务,所以在AsyncTask默认启用的是单线程执行,只有上一个任务执行后才会执行下一个任务。如果想要启用多线程执行任务,可以直接调用 executeOnExecutor(Executor exec, Params... params),这里的Executor参数可以使用AsyncTask自带的THREAD_POOL_EXECUTOR,也可以自己定义。

Handler

AsyncTask内部用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;          }      }  } 

如果消息类型是任务执行后的返回值(MESSAGE_POST_RESULT)将调用finish()方法:

private void finish(Result result) {      if (isCancelled()) {          onCancelled(result);      } else {          onPostExecute(result);      }      mStatus = Status.FINISHED;  } 

从上面可以知道,如果任务取消了,将调用onCancelled,否则调用onPostExecute,所以一个AsyncTask任务如果取消了,那么onPostExecute将不会得到执行。

如果消息类型是执行进度(MESSAGE_POST_PROGRESS)将调用onProgressUpdate,这个方法默认是空方法,我们可以根据自己的需要重写。

总结

AsyncTask的主要逻辑就如上面所分析的,总结几个需要注意的地方:

  • AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)

  • AsyncTask对象必须在UI线程创建

  • execute方法必须在UI线程调用

  • 不要手动调用onPreExecute()doInBackgroundonProgressUpdate方法

  • 一个任务只能被调用一次(第二次调用会抛出异常)

其它还有一些细节可以自行研究源码,另外推荐几篇不错的文章:

  1. Android源码分析—带你认识不一样的AsyncTask

  2. Android AsyncTask完全解析,带你从源码的角度彻底理解


原创粉丝点击