編輯:關於Android編程
最近項目發現個重大問題,結果打log跟蹤查是AsyncTask導致的。如果對AsyncTask了解的不夠深入透徹,那寫代碼就是埋雷。以後不定在哪個時間爆炸。首先我們要了解,谷歌為什麼發明AsyncTask,AsyncTask到底是用來解決什麼問題的?Android有一個原則---單線程模型的原則:UI操作並不是線程安全的並且這些操作必須在UI線程中執行。
在單線程模型中始終要記住兩條法則:
1. 不要阻塞UI線程
2. 確保只在UI線程中訪問Android UI工具包
首先來說說AsyncTask重寫的4個方法:
(1)doInBackground() //運行在後台線程中
(2)onPreExecute() //運行在UI線程中
(3)onProgressUpdate() //運行在UI線程中
(4)onPostExecute() //運行在UI線程中
詳細的這幾個方法怎麼使用,具體不清楚的可以谷歌一下,好多同仁講的好詳細的;
為了正確的使用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}.) * *Here is an example of subclassing:
** private class DownloadFilesTask extends AsyncTask這個是AsyncTask中給出的demo,看到{ * 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"); * } * } if (isCancelled()) break;這個就是用來判斷是否退出後台線程,如果設置了cancel(true),就break跳出循環。在這個地方多說2點:
(1)終止線程最好不要用打斷線程來做,這樣的方式太粗暴了,而且不能保證代碼的完整性,最好的處理方式就是在for循環,while循環中加入自己的判斷標志位,就像AsyncTask這種方法來處理是最好的,這也是谷歌來指導我們怎麼來處理終止線程的辦法。
(2)也有同學感到疑惑,說我的代碼就沒有循環,怎麼來加標志位,其實這個一般來說後台處理,大部分都是處理循環的邏輯,很少說一行代碼或者十幾行代碼很耗時的,(當然網絡相關的另說了,還有下載相關的,這個有其他方法來解決的)。即使有的話,比如調用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()中的循環語句中加入@Override protected Integer doInBackground(Object... arg0) { List這樣,邏輯按照需求來寫,需求是什麼樣子的,邏輯就相應的怎麼處理;contacts = new ArrayList (); 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這個就是我的模型,大家看懂後,就可以套到自己的代碼中去了。這是我用來動態加載刷新UI的邏輯,刷新出10個數據就刷新一次,這樣就可以避免次次刷新影響效率,又能保證用戶不必等到數據都加載完才能看到數據。一舉兩得。mContacts = new ArrayList (); private class ContactsListLoader extends AsyncTask 要想了解更多AsyncTask,就只能參考源碼了。
首先新建了一個項目用來演示集成ShareSDK 下載好了ShareSDK之後,解壓sharesd
問題的闡述:Android SDK中的HttpClient和HttpUrlConnection兩種請求方式用來處理網絡的復雜的操作,但當應用比較復雜的時候需要我們編寫大量
Android LayoutInflater.inflate()詳解深入理解LayoutInflater.inflate()由於我們很容易習慣公式化的預置代碼
有幾天沒有更新博客了,不過本篇卻准備了許久,希望能帶給每一位開發者最簡單高效的學習方式。廢話到此為止,下面開始正文。 NFC(Near Field Communicati