Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> AsyncTask粗糙講解

AsyncTask粗糙講解

編輯:關於Android編程

Asynctask的使用

AsyncTask是一個抽象的類,並且要為其指定三個泛型參數,這三個參數分別是

Params:這個是在執行時需要的參數,在後台任務中使用’
Progress: 如果需要返回進度是,在後台任務執行,返回的任務進行的進度
Result:當後台任務完成時,返回的結果

下面是一個簡單的實例
“` java
public class CustomAsynctask extends AsyncTask

public class CustomAsynctask extends AsyncTask{
  @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的使用環境以及使用方法都講明白了:

/**
 * 

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.

*AsyncTask是用在UI線程並且很簡單,它允許你在後台執行操作並將結果發布在UI線程並且不需要使用多線程和handler。 *

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 java.util.concurrent package such as {@link Executor}, * {@link ThreadPoolExecutor} and {@link FutureTask}.

*Asynctask通過thread和handler變成一個幫助類,但他並不是一個普遍的多線程框架,Asynctask應該用在那些耗時較短的操作(比如說最多幾秒鐘),如果你需要 * 讓線程運行較長時間,非常建議你用java提供的並發包,比如使用Executor、ThreadPoolExecutor和FutureTask。 *

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 Params, Progress and Result, * and 4 steps, called onPreExecute, doInBackground, * onProgressUpdate and onPostExecute.

*一個異步任務是在後台進行機選並且把結果發布在UI線程。一個異步任務需要定義三個泛型變量:Params、Progress、Result 和四個方法onPreExecute、doInBackground、onProgressUpdate、onPostExecute。 * *

Developer Guides

*

For more information about using tasks and threads, read the * Processes and * Threads developer guide.

* * *

Usage

*

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}.)

*Asynctask必須要繼承才能夠使用,並且這個子類需要復寫至少一個方法doInBackground,經常需要復寫另外一個onPostExecute 下面是簡單的使用方法 *

Here is an example of subclassing:

*

 * private class DownloadFilesTask extends AsyncTask {
 *     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");
 *     }
 * }
 * 
* *

Once created, a task is executed very simply:

*

 * new DownloadFilesTask().execute(url1, url2, url3);
 * 
*一旦生成,asynctask使用起來非常簡單 *

AsyncTask's generic types

*

The three types used by an asynchronous task are the following:

*
  1. *
    • Params, the type of the parameters sent to the task upon * execution.
    • 需要傳給task的變量類型 *
      • Progress, the type of the progress units published during * the background computation.
      • 在後台執行的時候,需要發布的進度單位 *
        • Result, the type of the result of the background * computation.
        • 後台計算完成後,這個結果的類型 *
*

Not all types are always used by an asynchronous task. To mark a type as unused, * simply use the type {@link Void}:

這些變量經常使用,如果不想傳入某個變量,只需傳入Void即可 * * private class MyTask extends AsyncTask { ... } * * *

The 4 steps

*

When an asynchronous task is executed, the task goes through 4 steps:

*
  1. 一旦這個異步任務執行了,需要經歷四個步驟 *
  2. {@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.
  3. 在任務執行前在UI線程調用,這個方法通常用來設置這個任務,比如說在顯示一個進度條給用戶 *
  4. {@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.
  5. 這個通常實在onPreExecute結束之後被後台線程調用,這個方法只要是用來處理一下比較耗時的計算。這個異步任務的變量就是傳給這個方法, 計算的結果也必須要返回並且把這個方法的返回值傳給最後一個方法,在這個方法裡可以調用publishProgress來發布進度,這些進度值將在onProgressUpdate 裡顯示給UI線程 *
  6. {@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.
  7. 在調用publishProgress之後再UI線程被調用,並沒有什麼固定的執行時機,這個方法通常用來在後台計算時在UI線程裡 展示一下進度,比如用一個progressbar的動畫或者打印些log *
  8. {@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.
  9. 在後台任務執行完畢之後在UI線程調用,後台任務的結果也將作為一個變量傳進來 *
* *

Cancelling a task

*

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.)

* 一個任務是可以通過調用cancel方法在任何時間取消,通過調用這個方法也會引起isCancelled返回true 在調用這個方法的時候,onPostExecute並不會在doInBackground方法執行完畢之後執行,取而代之的是執行onCancelled方法 為了確保這個任務盡快的取消,你應該要在doInBackground中周期性的確認isCancelled的返回值。 *

Threading rules

*

There are a few threading rules that must be followed for this class to * work properly:

*
  • *
  • The AsyncTask class must be loaded on the UI thread. This is done * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.
  • *
  • The task instance must be created on the UI thread.
  • *
  • {@link #execute} must be invoked on the UI thread.
  • *
  • Do not call {@link #onPreExecute()}, {@link #onPostExecute}, * {@link #doInBackground}, {@link #onProgressUpdate} manually.
  • *
  • The task can be executed only once (an exception will be thrown if * a second execution is attempted.)
  • *
* 為了讓這個類能夠正確的執行,需要遵循幾個線程規則 這個類必須在UI線程加載 這個類必須在UI線程實例化 這個類的excute方法必須是在UI線程調用 不要手動調用onPreExecute、doInBackground、onPostExecute、onProgressUpdate這幾個方法 一個任務只能被執行一次,如果執行兩次會拋出異常 *

Memory observability

*

AsyncTask guarantees that all callback calls are synchronized in such a way that the following * operations are safe without explicit synchronizations.

*
  • *
  • Set member fields in the constructor or {@link #onPreExecute}, and refer to them * in {@link #doInBackground}. *
  • Set member fields in {@link #doInBackground}, and refer to them in * {@link #onProgressUpdate} and {@link #onPostExecute}. *
* *

Order of execution

*

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.

*

If you truly want parallel execution, you can invoke * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with * {@link #THREAD_POOL_EXECUTOR}.

在Asynctask剛出現的時候,Asynctask是在一個單一的後台進程線性執行,自從donut之後改成了線程池,允許多條線程並行執行,之後honeycomb 改成了單一線程執行,這樣式喂了比意安因為並行執行導致的許多普遍的錯誤,如果你需要並行執行,你只需要調用executeOnExecutor(java.util.concurrent.Executor, Object[]) */

是不是講的很詳細 很詳細
RTFSC還是很管用的啊
下面 我們按照執行的步驟來讀一下流程
有請 構造方法

private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
private final WorkerRunnable mWorker;
private final FutureTask mFuture;
public AsyncTask() {
       mWorker = new WorkerRunnable() {
           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(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(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 execute(Params... params) {
       return executeOnExecutor(sDefaultExecutor, params);
   }

@MainThread
  public static void execute(Runnable runnable) {
      sDefaultExecutor.execute(runnable);
  }
  @MainThread
  public final AsyncTask 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 mTasks = new ArrayDeque();
       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,任務開始啟動。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved