AsyncTask粗糙讲解

Posted Sun_TTTT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AsyncTask粗糙讲解相关的知识,希望对你有一定的参考价值。

Asynctask源码分析

Asynctask的使用

AsyncTask是一个抽象的类,并且要为其指定三个泛型参数,这三个参数分别是

Params:这个是在执行时需要的参数,在后台任务中使用’
Progress: 如果需要返回进度是,在后台任务执行,返回的任务进行的进度
Result:当后台任务完成时,返回的结果

下面是一个简单的实例
“` java
public class CustomAsynctask extends AsyncTask

public class CustomAsynctask extends AsyncTask<Void,Integer,Object>
  @override
  protected void onPreExecute()
//在UI进程执行,可以调用UI控件,如显示progressbar

  
  @override
  protected Object doInBackground(Void... params)
//在子线程进行操作,不可调用UI控件,执行任务的地方
//可以调用publishProgress()方法调用onProgressUpdate()进行更新进度操作
  return null;
  
  @override
  protected void onProgressUpdate(Integer... valus)
//更新操作,在UI线程,可以操作控件
  
  @override
  protected void onPostExecute(Object object)
//在doInBackground()方法完成之后调用,显示结果等,也是在UI线程,可以调用控件
  

下面是执行的方法

new customAsynctask().execute();

源码分析

下面我们将根据流程来分析一下源码,不过在读源码之前,我们还是要看一下源码文件里面的介绍,其实这个介绍就将Asynctask的使用环境以及使用方法都讲明白了:

/**
 * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
 * perform background operations and publish results on the UI thread without
 * having to manipulate threads and/or handlers.</p>
 *AsyncTask是用在UI线程并且很简单,它允许你在后台执行操作并将结果发布在UI线程并且不需要使用多线程和handler。
 * <p>AsyncTask is designed to be a helper class around @link Thread and @link Handler
 * and does not constitute a generic threading framework. AsyncTasks should ideally be
 * used for short operations (a few seconds at the most.) If you need to keep threads
 * running for long periods of time, it is highly recommended you use the various APIs
 * provided by the <code>java.util.concurrent</code> package such as @link Executor,
 * @link ThreadPoolExecutor and @link FutureTask.</p>
 *Asynctask通过thread和handler变成一个帮助类,但他并不是一个普遍的多线程框架,Asynctask应该用在那些耗时较短的操作(比如说最多几秒钟),如果你需要
* 让线程运行较长时间,非常建议你用java提供的并发包,比如使用Executor、ThreadPoolExecutor和FutureTask。
 * <p>An asynchronous task is defined by a computation that runs on a background thread and
 * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
 * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
 * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
 * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
 *一个异步任务是在后台进行机选并且把结果发布在UI线程。一个异步任务需要定义三个泛型变量:Params、Progress、Result
 和四个方法onPreExecute、doInBackground、onProgressUpdate、onPostExecute。
 * <div class="special reference">
 * <h3>Developer Guides</h3>
 * <p>For more information about using tasks and threads, read the
 * <a href="@docRootguide/topics/fundamentals/processes-and-threads.html">Processes and
 * Threads</a> developer guide.</p>
 * </div>
 *
 * <h2>Usage</h2>
 * <p>AsyncTask must be subclassed to be used. The subclass will override at least
 * one method (@link #doInBackground), and most often will override a
 * second one (@link #onPostExecute.)</p>
 *Asynctask必须要继承才能够使用,并且这个子类需要复写至少一个方法doInBackground,经常需要复写另外一个onPostExecute
 下面是简单的使用方法
 * <p>Here is an example of subclassing:</p>
 * <pre class="prettyprint">
 * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> 
 *     protected Long doInBackground(URL... urls) 
 *         int count = urls.length;
 *         long totalSize = 0;
 *         for (int i = 0; i < count; i++) 
 *             totalSize += Downloader.downloadFile(urls[i]);
 *             publishProgress((int) ((i / (float) count) * 100));
 *             // Escape early if cancel() is called
 *             if (isCancelled()) break;
 *         
 *         return totalSize;
 *     
 *
 *     protected void onProgressUpdate(Integer... progress) 
 *         setProgressPercent(progress[0]);
 *     
 *
 *     protected void onPostExecute(Long result) 
 *         showDialog("Downloaded " + result + " bytes");
 *     
 * 
 * </pre>
 *
 * <p>Once created, a task is executed very simply:</p>
 * <pre class="prettyprint">
 * new DownloadFilesTask().execute(url1, url2, url3);
 * </pre>
 *一旦生成,asynctask使用起来非常简单
 * <h2>AsyncTask's generic types</h2>
 * <p>The three types used by an asynchronous task are the following:</p>
 * <ol>
 *     <li><code>Params</code>, the type of the parameters sent to the task upon
 *     execution.</li>
 需要传给task的变量类型
 *     <li><code>Progress</code>, the type of the progress units published during
 *     the background computation.</li>
 在后台执行的时候,需要发布的进度单位
 *     <li><code>Result</code>, the type of the result of the background
 *     computation.</li>
 后台计算完成后,这个结果的类型
 * </ol>
 * <p>Not all types are always used by an asynchronous task. To mark a type as unused,
 * simply use the type @link Void:</p>
 这些变量经常使用,如果不想传入某个变量,只需传入Void即可
 * 
 * private class MyTask extends AsyncTask<Void, Void, Void>  ... 
 * 
 *
 * <h2>The 4 steps</h2>
 * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
 * <ol>
 一旦这个异步任务执行了,需要经历四个步骤
 *     <li>@link #onPreExecute(), invoked on the UI thread before the task
 *     is executed. This step is normally used to setup the task, for instance by
 *     showing a progress bar in the user interface.</li>
 在任务执行前在UI线程调用,这个方法通常用来设置这个任务,比如说在显示一个进度条给用户
 *     <li>@link #doInBackground, invoked on the background thread
 *     immediately after @link #onPreExecute() finishes executing. This step is used
 *     to perform background computation that can take a long time. The parameters
 *     of the asynchronous task are passed to this step. The result of the computation must
 *     be returned by this step and will be passed back to the last step. This step
 *     can also use @link #publishProgress to publish one or more units
 *     of progress. These values are published on the UI thread, in the
 *     @link #onProgressUpdate step.</li>

 这个通常实在onPreExecute结束之后被后台线程调用,这个方法只要是用来处理一下比较耗时的计算。这个异步任务的变量就是传给这个方法,
 计算的结果也必须要返回并且把这个方法的返回值传给最后一个方法,在这个方法里可以调用publishProgress来发布进度,这些进度值将在onProgressUpdate
 里显示给UI线程

 *     <li>@link #onProgressUpdate, invoked on the UI thread after a
 *     call to @link #publishProgress. The timing of the execution is
 *     undefined. This method is used to display any form of progress in the user
 *     interface while the background computation is still executing. For instance,
 *     it can be used to animate a progress bar or show logs in a text field.</li>
 在调用publishProgress之后再UI线程被调用,并没有什么固定的执行时机,这个方法通常用来在后台计算时在UI线程里
 展示一下进度,比如用一个progressbar的动画或者打印些log
 *     <li>@link #onPostExecute, invoked on the UI thread after the background
 *     computation finishes. The result of the background computation is passed to
 *     this step as a parameter.</li>
 在后台任务执行完毕之后在UI线程调用,后台任务的结果也将作为一个变量传进来
 * </ol>
 * 
 * <h2>Cancelling a task</h2>
 * <p>A task can be cancelled at any time by invoking @link #cancel(boolean). Invoking
 * this method will cause subsequent calls to @link #isCancelled() to return true.
 * After invoking this method, @link #onCancelled(Object), instead of
 * @link #onPostExecute(Object) will be invoked after @link #doInBackground(Object[])
 * returns. To ensure that a task is cancelled as quickly as possible, you should always
 * check the return value of @link #isCancelled() periodically from
 * @link #doInBackground(Object[]), if possible (inside a loop for instance.)</p>
 *
 一个任务是可以通过调用cancel方法在任何时间取消,通过调用这个方法也会引起isCancelled返回true
 在调用这个方法的时候,onPostExecute并不会在doInBackground方法执行完毕之后执行,取而代之的是执行onCancelled方法
 为了确保这个任务尽快的取消,你应该要在doInBackground中周期性的确认isCancelled的返回值。
 * <h2>Threading rules</h2>
 * <p>There are a few threading rules that must be followed for this class to
 * work properly:</p>
 * <ul>
 *     <li>The AsyncTask class must be loaded on the UI thread. This is done
 *     automatically as of @link android.os.Build.VERSION_CODES#JELLY_BEAN.</li>
 *     <li>The task instance must be created on the UI thread.</li>
 *     <li>@link #execute must be invoked on the UI thread.</li>
 *     <li>Do not call @link #onPreExecute(), @link #onPostExecute,
 *     @link #doInBackground, @link #onProgressUpdate manually.</li>
 *     <li>The task can be executed only once (an exception will be thrown if
 *     a second execution is attempted.)</li>
 * </ul>
 *
 为了让这个类能够正确的执行,需要遵循几个线程规则

 这个类必须在UI线程加载
 这个类必须在UI线程实例化
 这个类的excute方法必须是在UI线程调用
 不要手动调用onPreExecute、doInBackground、onPostExecute、onProgressUpdate这几个方法
 一个任务只能被执行一次,如果执行两次会抛出异常
 * <h2>Memory observability</h2>
 * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
 * operations are safe without explicit synchronizations.</p>
 * <ul>
 *     <li>Set member fields in the constructor or @link #onPreExecute, and refer to them
 *     in @link #doInBackground.
 *     <li>Set member fields in @link #doInBackground, and refer to them in
 *     @link #onProgressUpdate and @link #onPostExecute.
 * </ul>
 *
 * <h2>Order of execution</h2>
 * <p>When first introduced, AsyncTasks were executed serially on a single background
 * thread. Starting with @link android.os.Build.VERSION_CODES#DONUT, this was changed
 * to a pool of threads allowing multiple tasks to operate in parallel. Starting with
 * @link android.os.Build.VERSION_CODES#HONEYCOMB, tasks are executed on a single
 * thread to avoid common application errors caused by parallel execution.</p>
 * <p>If you truly want parallel execution, you can invoke
 * @link #executeOnExecutor(java.util.concurrent.Executor, Object[]) with
 * @link #THREAD_POOL_EXECUTOR.</p>
 在Asynctask刚出现的时候,Asynctask是在一个单一的后台进程线性执行,自从donut之后改成了线程池,允许多条线程并行执行,之后honeycomb
 改成了单一线程执行,这样式喂了比意安因为并行执行导致的许多普遍的错误,如果你需要并行执行,你只需要调用executeOnExecutor(java.util.concurrent.Executor, Object[])

 */

是不是讲的很详细 很详细
RTFSC还是很管用的啊
下面 我们按照执行的步骤来读一下流程
有请 构造方法

private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
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);
               //调用doInBackground方法
               Binder.flushPendingCommands();
               return postResult(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);
               
           
       ;
   
   private void postResultIfNotInvoked(Result result) 
           final boolean wasTaskInvoked = mTaskInvoked.get();
           if (!wasTaskInvoked) 
               postResult(result);
           
       

       private Result postResult(Result result) 
           @SuppressWarnings("unchecked")
           Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                   new AsyncTaskResult<Result>(this, result));
           message.sendToTarget();
           return result;
       
       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;
                
            
        
    
    private void finish(Result result) 
       if (isCancelled()) 
           onCancelled(result);
        else 
           onPostExecute(result);
       
       mStatus = Status.FINISHED;
   

构造方法先实例了两个变量 一个类型是Callable另一个类型是Futuretask 一个用来后台执行,另一个用来监视后台执行的情况,在mWorker里我们见到了
熟悉的doInBackground方法,他将执行的结果调传给postresult方法,postResult方法内将结果用handler发送给UI线程,UI线程接收到后调用finish方法
finish方法判断线程是否取消如果没有取消就调用onPostExecute方法,如果取消就调用OnCancelled方法

下面我们再看看excute方法

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
@MainThread
   public final AsyncTask<Params, Progress, Result> execute(Params... params) 
       return executeOnExecutor(sDefaultExecutor, params);
   

@MainThread
  public static void execute(Runnable runnable) 
      sDefaultExecutor.execute(runnable);
  
  @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;
  
  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);
           
       
   

execute方法直接调用了executeOnExecutor方法,并向里面传入了sDefaultExecutor 这是个什么东西我们看一下。
这是一个为了保证runnable能够线性执行的Executor。它在执行是,先向mTasks这个队列的尾部放入一个runnable
而这个runnable先让传入的runnable r run起来 然后在执行schedulenext方法 这个方法是什么东西。这个方法只是简单的判断一下如果mTasks里面是否为空,如果不为空,则取出
第一个元素并赋值给mActive 然后用THREAD_POOL_EXECUTOR来执行。没看懂?我们按照使用时流程走一下遍

execute(a);
execute(b);
execute(c);

开始走了哈

先是向mTask里面放入三个runnable d、e、f。然后判断mActive是否为空(此时为空),如果为空,则执行scheduleNext方法
从mTasks的前面取出一个元素(也就是d),并赋值给mActive,然后用THREAD_POOL_EXECUTOR去执行mActive 也就是执行d,
d的run方法有一个try块 try块里面执行a的run方法 然后在调用scheduleNext方法,这一次mActive变成了e,继续循环。
直到mTasks里面没有了元素。

这个也就是说,因为这个是线性的在执行,所以并没有执行的数量限制,在3.0之前并没有这个SerialExecutor,而是使用的普通的线程池,并且规定了最大的线程数为128
所以不能超过128条线程,自从加入了SerialExecutor之后,这个线程池的数量限制便没有了。

在继续接上面,在调用executeOnExecutor方法,先判断mStatus是否在等待 如果不是,则抛出异常,然后将mStatus改为RUNNING,然后执行onPreExecute方法,
再将变量传给mWorker,然后执行mFuture,任务开始启动。

以上是关于AsyncTask粗糙讲解的主要内容,如果未能解决你的问题,请参考以下文章

AsyncTask doInBackground 返回数组列表

正确使用AsyncTask get()

如何使用 AsyncTask 更新全局变量

ArrayList.add 不适用于 AsyncTask

在AsyncTask中更新UI

粗糙分析设计模式——单例模式