Android非同步載入AsyncTask詳解

來源:互聯網
上載者:User

標籤:android   style   blog   http   io   color   ar   os   使用   

      最近項目發現個重大問題,結果打log跟蹤查是AsyncTask導致的。如果對AsyncTask瞭解的不夠深入透徹,那寫代碼就是埋雷。以後不定在哪個時間爆炸。首先我們要瞭解,Google為什麼發明AsyncTask,AsyncTask到底是用來解決什麼問題的?Android有一個原則---單執行緒模式的原則:UI操作並不是安全執行緒的並且這些操作必須在UI線程中執行。

在單執行緒模式中始終要記住兩條法則:
1. 不要阻塞UI線程
2. 確保只在UI線程中訪問Android UI工具包


首先來說說AsyncTask重寫的4個方法:

(1)doInBackground()  //運行在後台線程中

(2)onPreExecute()  //運行在UI線程中

(3)onProgressUpdate()  //運行在UI線程中

(4)onPostExecute()  //運行在UI線程中

 詳細的這幾個方法怎麼使用,具體不清楚的可以Google一下,好多同仁講的好詳細的;


為了正確的使用AsyncTask類,以下是幾條必須遵守的準則:
1) Task的執行個體必須在UI thread中建立
2) execute方法必須在UI thread中調用
3) 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法
4) 該task只能被執行一次,否則多次調用時將會出現異常
doInBackground方法和onPostExecute的參數必須對應,這兩個參數在AsyncTask聲明的泛型參數列表中指定,第一個為doInBackground 接受的參數,第二個為顯示進度的參數,第第三個為doInBackground返回和onPostExecute傳入的參數。


     以上四點是我從網摘過來的,我覺得說的有道理,針對第4點,我有異議:即使多次調用,也不應該出現異常,因為AsyncTask類有對外公開的介面,cancel(true),isCancelled()。這兩個方法,這兩個方法配合使用就來控制AsyncTask可以手動退出。具體可以參照AsyncTask.java這個源碼類中有例子的。

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> * * <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"); *     } * }
這個是AsyncTask中給出的demo,看到
 if (isCancelled()) break;

這個就是用來判斷是否退出後台線程,如果設定了cancel(true),就break跳出迴圈。在這個地方多說2點:

(1)終止線程最好不要用打斷線程來做,這樣的方式太粗暴了,而且不能保證代碼的完整性,最好的處理方式就是在for迴圈,while迴圈中加入自己的判斷標誌位,就像AsyncTask這種方法來處理是最好的,這也是Google來指導我們怎麼來處理終止線程的辦法。

(2)也有同學感到疑惑,說My Code就沒有迴圈,怎麼來加標誌位,其實這個一般來說幕後處理,大部分都是處理迴圈的邏輯,很少說一行代碼或者十幾行代碼很耗時的,(當然網路相關的另說了,還有下載相關的,這個有其他方法來解決的)。即使有的話,比如調用jni,so庫,返回就是慢。那就在幾個耗時的方法的後面都加上標誌位的判斷;

通過上述方法就可以做出完整的方案設計,就能設計,當下次再次執行AsyncTask,先判斷自己是否正在運行,如果在運行,就不執行或取消任務重新執行,這個要看具體的需求是什麼了;

舉個栗子:

private void stopAyncTaskRunning() {if (mContactsListLoader != null&& mContactsListLoader.getStatus() == AsyncTask.Status.RUNNING) {mContactsListLoader.cancel(true); //if task is still running, stop it;}}

private void getContactsList() {stopAyncTaskRunning();mContactsListLoader = new ContactsListLoader();mContactsListLoader.executeOnExecutor(AsyncTask.THEAD_POOL_EXECUTOR);}
當然,我的這個需求是下次進來的時候,就取消上次的任務,然後重新重新整理資料。另外也不要忘記在doInBackground()中的迴圈語句中加入
@Overrideprotected Integer doInBackground(Object... arg0) {List<Contact> contacts = new ArrayList<Contact>();ContentResolver cr = mContext.getContentResolver();Cursor c = cr.query(ContactsContract.Contacts.CONTENT_URI,PROJECTION_CONTACT, null, null, null);if (c != null) {c.moveToPosition(-1);while (c.moveToNext()) {if (isCancelled()) {break;}。。。 。。。}
這樣,邏輯按照需求來寫,需求是什麼樣子的,邏輯就相應的怎麼處理;


    再來說說AsyncTask坑人的地方,就是在Android3.0以後的版本,AsyncTask的執行方法分為2個了:

    (1)execute()
    (2)executeOnExecutor()

    如果用AsyncTask調用(1)的時候,就表示串列執行線程,如果這個Activity中有4個fragment,而且每個fragment都有一個AsyncTask,這樣的話用(1)的話,就必須順序執行,等一個執行完,第二個才執行。如果用方法(2),則可以串列執行,這個UI效果就很好了。線程池可以用系統的,也可以用我們自訂的線程池;

另外對系統預設線程池中執行線程數的一些說明,如下:

下面的5代表corePoolSize,10代表阻塞隊列的長度,128代表maximumPoolSize
1:如果線程池的數量小於5,則建立新的線程並執行
2:如果線程數大於5且小於5+10(阻塞隊列大小),則將第6~15的線程加入阻塞隊列,待線程池中的5個正在啟動並執行線程有某個結束後,取出阻塞隊列的線程執行。
3:如果線程數為16~128,則啟動並執行線程數為num-10
4:如果線程數大於128,則捨棄。

詳細的的介紹可以參考部落格: Android實戰技巧:深入解析AsyncTask,這篇講解的很詳細;

   

   最後要說明的就是資料要載入一部分就重新整理UI,給使用者一個好的使用者體驗;舉個栗子,

private static final int DISPLAY_NUM = 10;private List<Contact> mContacts = new ArrayList<Contact>();private class ContactsListLoader extendsAsyncTask<Object, Integer, Integer> {int count = 0;List<Contact> mTempContacts = new ArrayList<Contact>(DISPLAY_NUM);@Overrideprotected void onPreExecute() {super.onPreExecute();mTempContacts.clear();mContacts.clear();}@Overrideprotected Integer doInBackground(Object... arg0) {List<Contact> contacts = new ArrayList<Contact>();if (c != null) {c.moveToPosition(-1);while (c.moveToNext()) {if (isCancelled()) {break;}... ...contacts.add(contact);mTempContacts.add(contact);if (++count >= DISPLAY_NUM) {publishProgress();count = 0;}}}return contacts.size();}@Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);mContacts.addAll(mTempContacts);mTempContacts.clear();mAdapter.notifyDataSetChanged();}@Overrideprotected void onPostExecute(Integer size) {if (isCancelled())return;if (size > 0) {if (mTempContacts.size() > 0&& mTempContacts.size() != DISPLAY_NUM) {mContacts.addAll(mTempContacts);}} else {if (mTempContacts.size() > 0) {mContacts.addAll(mTempContacts);}}mAdapter.notifyDataSetChanged();}}
這個就是我的模型,大家看懂後,就可以套到自己的代碼中去了。這是我用來動態載入重新整理UI的邏輯,重新整理出10個資料就重新整理一次,這樣就可以避免次次重新整理影響效率,又能保證使用者不必等到資料都載入完才能看到資料。一舉兩得。

      要想瞭解更多AsyncTask,就只能參考源碼了。

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.