非同步輕量級實現;
AsynceTask簡述:
1.功能類似於Handler,都是為了防止UI線程操作阻塞而衍生而來。
2.AsyncTask是Handler的一個輕量級實現,模型類似於IntentService於Service。都是為了更加方便操作。(因為一般的非同步,我們都是開啟一個子線程或是匿名線程,缺點就是樣的實現對於線程的操作,控制是十分困難)
3.闡述下Handler,一般我們就認為Handler既一個Android訊息處理器。預設情況下,他只接受當前線程的訊息執行個體。
但是,當在一個多線程,比如子線程資料處理後更新Ui線程,此時只要存在Handler的指標,簡單的說就是執行個體對象時,
訊息的收發處理就能執行在不同的進程中了,這個也是我們常用到的非同步處理手法。
4.從原始碼中看AsyncTask類中有 線程池,同樣也執行個體化了一個Handler對象。
說白了,AsyncTask只是對以上我們自己用handler,thread實現的非同步做了一個很好的封裝,使用到線程池對於線程的銷毀和建立開銷大大減小
綜合了下:AsyncTask的非同步處理相對於傳統的handler+Thread組合,減少程式中線程過多開銷過大。操作和管理更加方便。
AsyncTask的是實現:
和所有網上說的一樣,該對象必須在UiThread中執行個體化,然後執行execute方法。
copy下:AsyncTask定義了三種泛型型別 Params,Progress和Result。
•Params 啟動任務執行的輸入參數,比如HTTP請求的URL。
•Progress 背景工作執行的百分比。
•Result 後台執行任務最終返回的結果,比如String。
AsyncTask的執行分為四個步驟,每一步都對應一個回調方法,開發人員需要實現一個或幾個方法。在任務的執行過程中,這些方法被自動調用。
onPreExecute(), 該方法將在執行實際的後台操作前被UI thread調用。可以在該方法中做一些準備工作,如在介面上顯示一個進度條。
doInBackground(Params...), 將在onPreExecute 方法執行後馬上執行,該方法運行在後台線程中。這裡將主要負責執行那些很耗時的後台計算工作。可以調用 publishProgress方法來更新即時的任務進度。該方法是抽象方法,子類必須實現。
onProgressUpdate(Progress...),在publishProgress方法被調用後,UI thread將調用這個方法從而在介面上展示任務的進展情況,例如通過一個進度條進行展示。
onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,背景計算結果將通過該方法傳遞到UI thread.
使用AsyncTask類,以下是幾條必須遵守的準則:
1) Task的執行個體必須在UI thread中建立
2) execute方法必須在UI thread中調用
3) 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法
4) 該task只能被執行一次,否則多次調用時將會出現異常
下面是我看了原始碼書寫下整個流程:
step 1:在UiThread中執行個體化類 執行了父類的構造
public AsyncTask() { mWorker = new WorkerRunnable{//實現線程接扣的線程類。 public Result call() { return doInBackground(mParams);//在類似於線程的run方法中調用doInBackground(mParams); } }; mFuture = new FutureTask(mWorker) {//接受mWorker線程對象,執行個體化FutureTask類來操作線程。 }
step 2 :在UiThread中調用AsyncTask對象的execute方法。
public execute(){ onPreExecute();//此方法中先調用了onPreExecute,也就是我們上文提到的準備工作。 sExecutor.execute(mFuture);// 然後通過線程池操作構造中我們執行個體化的runnable對象。 return this;
step 3 :根據step 2 可以知道 程式這步執行的應該是mFuture 代碼中的done()
protected void done() { Message message; Result result = null; // 發送訊息Tag和 訊息處理結果。訊息處理結果又用AsyncTaskResult類封裝起來,執行個體化對象傳遞進去的result就是上面doInBackground方法執行後的結果。 message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(AsyncTask.this, result)); message.sendToTarget(); }
step 4: 根據step 3,很明顯 handler發送,後緊接著就是訊息的處理
private static class InternalHandler extends Handler { @Override public void handleMessage(Message msg) { AsyncTaskResult result = (AsyncTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT://通過step 3的Tag,執行改方法; // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; case MESSAGE_POST_CANCEL: result.mTask.onCancelled(); break; } } }
step 5: 根據step 4執行 result.mTask.finish
private void finish(Result result) { if (isCancelled()) result = null; onPostExecute(result);// 注意。。執行了onPostExecute mStatus = Status.FINISHED; }
至此我們發現上文提到
onPreExecute() ----》doInBackground()------》onPostExecute()基本流程方法都遊走一邊
還剩下兩個重新整理進度的 : onProgressUpdate()和publishProgress();
protected final void publishProgress(Progress... values) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); }
從這個方法很容易看出,其實他就是一個Handler訊息發送的實現。
網上一般說可以在doInBackground()調用該方法,然後再回過去看handler執行個體化那段代碼
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
執行了onProgressUpdate()
該類出現了FutureTask以及callable等java中的類。因為手邊沒有java 源碼。沒有細細讀下
整個流程大致如上。
下面附上一個用過的,也算比較常用的首頁圖片非同步擷取例子代碼:
public class MainActivity extends BaseActivity implements OnClickListener { private static final HashMap<String, Drawable> HOMEIMAGECACHE = new HashMap<String, Drawable>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext = MainActivity.this; FrameLayout ll = (FrameLayout) this.getLayoutInflater().inflate( R.layout.main, null); setContentView(ll); init(); fatchData(); } private void fatchData() { GetHomePageImageTask imageTask = new GetHomePageImageTask(); //首頁圖片擷取測試 String imgUrl = "http://192.168.1.1/img/xxxxx.png,http://192.168.1.1/img/zuqiu2.png,http://192.168.1.1/img/zuqiu3.png"; imageTask.execute(imgUrl); } private void init() { loadView(); } private Gallery mGallery; /** * */ private void loadView() { mGallery = (Gallery) findViewById(R.id.homepage_gallery); } class GetHomePageImageTask extends AsyncTask<String, Integer, Drawable []>{ /** * step 1: 在ui Thread中調用execute()後執行該方法; * 一般可以做些準備工作。 */ @Override protected void onPreExecute() { // TODO Auto-generated method stub super.onPreExecute(); } /** * step 2: 執行後台操作; */ @Override protected Drawable[] doInBackground(String... params) { String [] imageUrls= params[0].split(","); Drawable [] drawables = new Drawable[imageUrls.length]; for(int i = 0; i < imageUrls.length;i++ ){ drawables[i] = lookupFile(imageUrls[i]); } return drawables; } /** *在publishProgress方法被調用後,ui線程將調用該方法更新進度; */ @Override protected void onProgressUpdate(Integer... values) { // TODO Auto-generated method stub super.onProgressUpdate(values); } /** * step 3:在doInBackground執行完成後i,該方法被Ui線程執行那個調用; */ @Override protected void onPostExecute(Drawable[] result) { // TODO Auto-generated method stub super.onPostExecute(result); ImageAdapter adapter = new ImageAdapter(mContext, result); mGallery.setAdapter(adapter); } } class ImageAdapter extends BaseAdapter { Holder holder; /* 聲明變數 */ int mGalleryItemBackground; private Context mContext; private Drawable[] _bitmap; LayoutInflater inflater; /* ImageAdapter的構造器 */ public ImageAdapter(Context c, Drawable[] bitmap) { mContext = c; _bitmap = bitmap; } /* 覆蓋的方法getCount,返回圖片數目 */ public int getCount() { return _bitmap.length; } /* 覆蓋的方法getItemId,返回映像的數組id */ public Object getItem(int position) { return position; } public long getItemId(int position) { return position; } /* 覆蓋的方法getView,返回一View對象 */ public View getView(int position, View convertView, ViewGroup parent) { inflater = LayoutInflater.from(mContext); if (convertView == null) { convertView = inflater.inflate(R.layout.game_detail_item, null); holder = new Holder(); holder.img = (ImageView) convertView .findViewById(R.id.game_gallery_img); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); } holder.img.setBackgroundDrawable(_bitmap[position]); return convertView; } final class Holder { ImageView img; } } /* * 圖片資料讀取 */ public Drawable lookupFile(String url) { // TODO Auto-generated method stub FileInputStream fis = null; Drawable drawable = null; try { if (HOMEIMAGECACHE.get(url) == null) { URL u = new URL(url); URLConnection openConnection = u.openConnection(); drawable = Drawable.createFromStream( openConnection.getInputStream(), ""); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); HOMEIMAGECACHE.put(url, drawable); } return HOMEIMAGECACHE.get(url); } catch (Exception e) { // Not there. return null; } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { } } } } }
程式碼分析:
1.url是裡包含多個圖片的網路地址
2.用了一個gallery來顯示圖片
3.實現自訂的GetHomePageImageTask
4.lookupFile()中用了下緩衝,可以忽略。
主要代碼還是GetHomePageImageTask中的
/** * step 2: 執行後台操作; */ @Override protected Drawable[] doInBackground(String... params) { String [] imageUrls= params[0].split(","); Drawable [] drawables = new Drawable[imageUrls.length]; for(int i = 0; i < imageUrls.length;i++ ){ drawables[i] = lookupFile(imageUrls[i]); } return drawables; }
/** * step 3:在doInBackground執行完成後i,該方法被Ui線程執行那個調用; */ @Override protected void onPostExecute(Drawable[] result) { // TODO Auto-generated method stub super.onPostExecute(result); ImageAdapter adapter = new ImageAdapter(mContext, result); mGallery.setAdapter(adapter); }