Android-多線程AsyncTask

來源:互聯網
上載者:User

標籤:

http://www.cnblogs.com/plokmju/p/android_AsyncTask.html

 

AsyncTask,非同步任務,可以簡單進行非同步作業,並把執行結果發布到UI主線程。AsyncTask是一個抽象類別,它的內部其實也是結合了Thread和Handler來實現非同步線程操作,但是它形成了一個通用線程架構,更清晰簡單。AsyncTask應該被用於比較簡短的操作(最多幾秒鐘)。如果需要保持長時間啟動並執行線程,可以使用ThreadPooExecutor或者FutureTask

 

首先來看一下AsyncTask的基本用法,由於AsyncTask是一個抽象類別,所以如果我們想使用它,就必須要建立一個子類去繼承它。在繼承時我們可以為AsyncTask類指定三個泛型參數,這三個參數的用途如下:

1. Params

在執行AsyncTask時需要傳入的參數,可用於在背景工作中使用。

2. Progress

背景工作執行時,如果需要在介面上顯示當前的進度,則使用這裡指定的泛型作為進度單位。

3. Result

當任務執行完畢後,如果需要對結果進行返回,則使用這裡指定的泛型作為傳回值類型。

 

AsyncTask的使用規則

  使用AsyncTask必須遵循以下規則:

  • AsyncTask必須聲明在UI線程上。
  • AsyncTask必須在UI線程上執行個體化。
  • 必須通過execute()方法執行任務。
  • 不可以直接調用onPreExecute()、onPostExecute(Resut)、doInBackground(Params...)、onProgressUpdate(Progress...)方法。
  • 可以設定任務只執行一次,如果企圖再次執行會報錯。

 

第一個因為AsyncTask內建了handler,所以在主線程運行最好。不過調用Looper.prepare後似乎也可以在子線程運行

 

使用方法在第一個連結裡很清楚,下面來看一下源碼:

http://blog.csdn.net/guolin_blog/article/details/11711405

 

  • 可以看到源碼execute時調用了executeOnExecutor()方法,在這裡調用了onPreExecute(),保證了他第一個執行
  • 接著調用了Executor的execute()方法,並將前面初始化的mFuture對象傳了進去
  • 接著就來到FutureTask類的run()方法,這裡開啟了一個子線程,調用call方法,而call方法調用了doInBackground()方法,這保證了方法在子線程裡執行
  • postResult()方法,源碼如下:
private Result postResult(Result result) {      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;          }      }  }  

  InernalHandler接受兩種資訊,如果這是一條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()方法,這樣當前任務的執行就全部結束了。

  從上面還可以看到,運行中可以隨時調用cancel(boolean)方法取消任務,如果成功調用isCancelled()會返回true,並且不會執行 onPostExecute() 方法了,取而代之的是調用 onCancelled() 方法。

  而且從源碼看,如果這個任務已經執行了這個時候調用cancel是不會真正的把task結束,而是繼續執行,只不過改變的是執行之後的回調方法是 onPostExecute還是onCancelled。

 

  onProgressUpdate()方法源碼如下:

protected final void publishProgress(Progress... values) {      if (!isCancelled()) {          sHandler.obtainMessage(MESSAGE_POST_PROGRESS,                  new AsyncTaskResult<Progress>(this, values)).sendToTarget();      }  }  

  同樣是發給handler處理,所以是在主線程啟動並執行

 

  SerialExecutor詳細:

   其實SerialExecutor也是AsyncTask在3.0版本以後做了最主要的修改的地方,它在AsyncTask中是以常量的形式被使用的,因此在整個應用程式中的所有AsyncTask執行個體都會共用同一個SerialExecutor

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是使用ArrayDeque這個隊列來管理Runnable對象的,如果我們一次性啟動了很多個任務,首先在第一次運行execute()方法的時候,會調用ArrayDeque的offer()方法將傳入的Runnable對象添加到隊列的尾部,然後判斷mActive對象是不是等於null,第一次運行當然是等於null了,於是會調用scheduleNext()方法。在這個方法中會從隊列的頭部取值,並賦值給mActive對象,然後調用THREAD_POOL_EXECUTOR去執行取出的取出的Runnable對象。之後如何又有新的任務被執行,同樣還會調用offer()方法將傳入的Runnable添加到隊列的尾部,但是再去給mActive對象做非空檢查的時候就會發現mActive對象已經不再是null了,於是就不會再調用scheduleNext()方法。

 

那麼後面添加的任務豈不是永遠得不到處理了?當然不是,看一看offer()方法裡傳入的Runnable匿名類,這裡使用了一個try finally代碼塊,並在finally中調用了scheduleNext()方法,保證無論發生什麼情況,這個方法都會被調用。也就是說,每次當一個任務執行完畢後,下一個任務才會得到執行,SerialExecutor模仿的是單一線程池的效果,如果我們快速地啟動了很多任務,同一時刻只會有一個線程正在執行,其餘的均處於等待狀態。Android照片牆應用實現,再多的圖片也不怕崩潰 這篇文章中例子的運行結果也證實了這個結論。

 

在Android 3.0之前是並沒有SerialExecutor這個類的,那個時候是直接在AsyncTask中構建了一個sExecutor常量,並對線程池總大小,同一時刻能夠啟動並執行線程數做了規定,代碼如下所示:

private static final int CORE_POOL_SIZE = 5;  private static final int MAXIMUM_POOL_SIZE = 128;  private static final int KEEP_ALIVE = 10;  ……  private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,          MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);  

可以看到,這裡規定同一時刻能夠啟動並執行線程數為5個,線程池總大小為128。也就是說當我們啟動了10個任務時,只有5個任務能夠立刻執行,另外的5個任務則需要等待,當有一個任務執行完畢後,第6個任務才會啟動,以此類推。而線程池中最大能存放的線程數是128個,當我們嘗試去添加第129個任務時,程式就會崩潰。

 

因此在3.0版本中AsyncTask的改動還是挺大的,在3.0之前的AsyncTask可以同時有5個任務在執行,而3.0之後的AsyncTask同時只能有1個任務在執行。為什麼升級之後可以同時執行的任務數反而變少了呢?這是因為更新後的AsyncTask已變得更加靈活,如果不想使用預設的線程池,還可以自由地進行配置。比如使用如下的代碼來啟動任務:

Executor exec = new ThreadPoolExecutor(15, 200, 10,          TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());  new DownloadTask().executeOnExecutor(exec);  

這樣就可以使用我們自訂的一個Executor來執行任務,而不是使用SerialExecutor。上述代碼的效果允許在同一時刻有15個任務正在執行,並且最多能夠儲存200個任務。

 

需要注意的地方:

http://www.open-open.com/lib/view/open1434802647364.html

上面提到了那麼多的注意點,還有其他需要注意的嗎?當然有!我們開發App過程中使用AsyncTask請求網路資料的時候,一般都是習慣在onPreExecute顯示進度條,在資料請求完成之後的onPostExecute關閉進度條。這樣做看似完美,但是如果您的App沒有明確指定螢幕方向和configChanges時,當使用者旋轉螢幕的時候Activity就會重新啟動,而這個時候您的非同步載入資料的線程可能正在請求網路。當一個新的Activity被重新建立之後,可能由重新啟動了一個新的任務去請求網路,這樣之前的一個非同步任務不經意間就泄露了,假設你還在onPostExecute寫了一些其他邏輯,這個時候就會發生意想不到異常。

一般簡單的資料類型的,對付configChanges我們很好處理,我們直接可以通過onSaveInstanceState()和onRestoreInstanceState()進行儲存與恢複。 Android會在銷毀你的Activity之前調用onSaveInstanceState()方法,於是,你可以在此方法中儲存關於應用狀態的資料。然後你可以在onCreate()或onRestoreInstanceState()方法中恢複。

但是,對於AsyncTask怎麼辦?問題產生的根源在於Activity銷毀重新建立的過程中AsyncTask和之前的Activity失聯,最終導致一些問題。那麼解決問題的思路也可以朝著這個方向發展。Android官方文檔 也有一些解決問題的線索。

這裡介紹另外一種使用事件匯流排的解決方案,是國外一個安卓大牛寫的。中間用到了Square開源的EventBus類庫http://square.github.io/otto/。首先自訂一個AsyncTask的子類,在onPostExecute方法中,把返回結果拋給事件匯流排,代碼如下:

@Override    protected String doInBackground(Void... params) {        Random random = new Random();        final long sleep = random.nextInt(10);        try {            Thread.sleep(10 * 6000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return "Slept for " + sleep + " seconds";    }    @Override    protected void onPostExecute(String result) {        MyBus.getInstance().post(new AsyncTaskResultEvent(result));    }

  在Activity的onCreate中註冊這個事件匯流排,這樣非同步線程的訊息就會被otta分發到當前註冊的activity,這個時候返回結果就在當前activity的onAsyncTaskResult中了,代碼如下:

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.otto_layout);        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {            @Override public void onClick(View v) {                new MyAsyncTask().execute();            }        });        MyBus.getInstance().register(this);    }    @Override    protected void onDestroy() {        MyBus.getInstance().unregister(this);        super.onDestroy();    }    @Subscribe    public void onAsyncTaskResult(AsyncTaskResultEvent event) {        Toast.makeText(this, event.getResult(), Toast.LENGTH_LONG).show();    }

  個人覺的這個方法相當好,當然更簡單的你也可以不用otta這個庫,自己單獨的用介面回調的方式估計也能實現,大家可以試試。

 

Android-多線程AsyncTask

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.