天天看點

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

android AsyncTask介紹

AsyncTask和Handler對比

1 ) AsyncTask實作的原理,和适用的優缺點

AsyncTask,是android提供的輕量級的異步類,可以直接繼承AsyncTask,在類中實作異步操作,并提供接口回報目前異步執行的程度(可以通過接口實作UI進度更新),最後回報執行的結果給UI主線程.

使用的優點:

l  簡單,快捷

l  過程可控

使用的缺點:

l  在使用多個異步操作和并需要進行Ui變更時,就變得複雜起來.

2 )Handler異步實作的原理和适用的優缺點

在Handler 異步實作時,涉及到 Handler, Looper, Message,Thread四個對象,實作異步的流程是主線程啟動Thread(子線程)àthread(子線程)運作并生成Message-àLooper擷取Message并傳遞給HandleràHandler逐個擷取Looper中的Message,并進行UI變更。

使用的優點:

l  結構清晰,功能定義明确

l  對于多個背景任務時,簡單,清晰

使用的缺點:

l  在單個背景異步處理時,顯得代碼過多,結構過于複雜(相對性)

  AsyncTask介紹 Android的AsyncTask比Handler更輕量級一些,适用于簡單的異步處理。 首先明确Android之是以有Handler和AsyncTask,都是為了不阻塞主線程(UI線程),且UI的更新隻能在主線程中完成,是以異步處理是不可避免的。  

Android為了降低這個開發難度,提供了AsyncTask。AsyncTask就是一個封裝過的背景任務類,顧名思義就是異步任務。

AsyncTask直接繼承于Object類,位置為android.os.AsyncTask。要使用AsyncTask工作我們要提供三個泛型參數,并重載幾個方法(至少重載一個)。

AsyncTask定義了三種泛型類型 Params,Progress和Result。

  • Params 啟動任務執行的輸入參數,比如HTTP請求的URL。
  • Progress 背景任務執行的百分比。
  • Result 背景執行任務最終傳回的結果,比如String。

使用過AsyncTask 的同學都知道一個異步加載資料最少要重寫以下這兩個方法:

  • doInBackground(Params…) 背景執行,比較耗時的操作都可以放在這裡。注意這裡不能直接操作UI。此方法在背景線程執行,完成任務的主要工作,通常需要較長的時間。在執行過程中可以調用publicProgress(Progress…)來更新任務的進度。
  • onPostExecute(Result)  相當于Handler 處理UI的方式,在這裡面可以使用在doInBackground 得到的結果處理操作UI。 此方法在主線程執行,任務執行的結果作為此方法的參數傳回

有必要的話你還得重寫以下這三個方法,但不是必須的:

  • onProgressUpdate(Progress…)   可以使用進度條增加使用者體驗度。 此方法在主線程執行,用于顯示任務執行的進度。
  • onPreExecute()        這裡是最終使用者調用Excute時的接口,當任務執行之前開始調用此方法,可以在這裡顯示進度對話框。
  • onCancelled()             使用者調用取消時,要做的操作

使用AsyncTask類,以下是幾條必須遵守的準則:

  • Task的執行個體必須在UI thread中建立;
  • execute方法必須在UI thread中調用;
  • 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法;
  • 該task隻能被執行一次,否則多次調用時将會出現異常;

一個超簡單的了解 AsyncTask 的例子:

main.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7.     <TextView    
  8.     android:id="@+id/textView01"  
  9.     android:layout_width="fill_parent"   
  10.     android:layout_height="wrap_content"   
  11.     />  
  12.    <ProgressBar   
  13.    android:id="@+id/progressBar02"  
  14.     android:layout_width="fill_parent"   
  15.     android:layout_height="wrap_content"   
  16.     style="?android:attr/progressBarStyleHorizontal"   
  17.     />  
  18.     <Button  
  19.     android:id="@+id/button03"  
  20.     android:layout_width="fill_parent"   
  21.     android:layout_height="wrap_content"   
  22.     android:text="更新progressbar"  
  23.     />  
  24. </LinearLayout>  

MainActivity.java

  1. package vic.wong.main;  
  2. import android.app.Activity;  
  3. import android.os.Bundle;  
  4. import android.view.View;  
  5. import android.view.View.OnClickListener;  
  6. import android.widget.Button;  
  7. import android.widget.ProgressBar;  
  8. import android.widget.TextView;  
  9. public class MainActivity extends Activity {  
  10.     private Button button;  
  11.     private ProgressBar progressBar;  
  12.     private TextView textView;  
  13.     @Override  
  14.     public void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.main);  
  17.         button = (Button)findViewById(R.id.button03);  
  18.         progressBar = (ProgressBar)findViewById(R.id.progressBar02);  
  19.         textView = (TextView)findViewById(R.id.textView01);  
  20.         button.setOnClickListener(new OnClickListener() {  
  21.             @Override  
  22.             public void onClick(View v) {  
  23.                 ProgressBarAsyncTask asyncTask = new ProgressBarAsyncTask(textView, progressBar);  
  24.                 asyncTask.execute(1000);  
  25.             }  
  26.         });  
  27.     }  
  28. }  

NetOperator.java

  1. package vic.wong.main;  
  2. //模拟網絡環境  
  3. public class NetOperator {  
  4.     public void operator(){  
  5.         try {  
  6.             //休眠1秒  
  7.             Thread.sleep(1000);  
  8.         } catch (InterruptedException e) {  
  9.             // TODO Auto-generated catch block  
  10.             e.printStackTrace();  
  11.         }  
  12.     }  
  13. }  

ProgressBarAsyncTask .java 

  1. package vic.wong.main;  
  2. import android.os.AsyncTask;  
  3. import android.widget.ProgressBar;  
  4. import android.widget.TextView;  
  5. public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {  
  6.     private TextView textView;  
  7.     private ProgressBar progressBar;  
  8.     public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {  
  9.         super();  
  10.         this.textView = textView;  
  11.         this.progressBar = progressBar;  
  12.     }  
  13.     @Override  
  14.     protected String doInBackground(Integer... params) {  
  15.         NetOperator netOperator = new NetOperator();  
  16.         int i = 0;  
  17.         for (i = 10; i <= 100; i+=10) {  
  18.             netOperator.operator();  
  19.             publishProgress(i);  
  20.         }  
  21.         return i + params[0].intValue() + "";  
  22.     }  
  23.     @Override  
  24.     protected void onPostExecute(String result) {  
  25.         textView.setText("異步操作執行結束" + result);  
  26.     }  
  27.     //該方法運作在UI線程當中,并且運作在UI線程當中 可以對UI空間進行設定  
  28.     @Override  
  29.     protected void onPreExecute() {  
  30.         textView.setText("開始執行異步線程");  
  31.     }  
  32.     @Override  
  33.     protected void onProgressUpdate(Integer... values) {  
  34.         int vlaue = values[0];  
  35.         progressBar.setProgress(vlaue);  
  36.     }  
  37. }  

分類:  Android之Handler 好文要頂  關注我  收藏該文 

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔
android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔
android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

Devin Zhang

關注 - 0

粉絲 - 979 +加關注 35 0 (請您對文章做出評價) « 上一篇: Android常用知識點總彙

» 下一篇: Android消息推送

posted on  2012-02-13 22:05  Devin Zhang 閱讀( 212470) 評論( 22)  編輯  收藏

評論: #1樓   2012-03-25 23:56 |  nickycookie   

publicProgress好像應該是publishProgress 支援(2) 反對(0)    #2樓   2012-08-28 22:03 |  scwsmile   

謝謝! 支援(0) 反對(0)    #3樓   2012-09-20 16:52 |  孤城雪飄   

講解的很清楚,感謝分享…… 支援(0) 反對(0)    #4樓   2012-10-30 11:18 |  singlecheng   

樓主寫的不錯,比較用心,最好再貼上源碼。嘿嘿 支援(0) 反對(1)    #5樓   2013-04-24 09:44 |  renfujiang   

•doInBackground(Params…) 背景執行,比較耗時的操作都可以放在這裡。注意這裡不能直接操作UI。此方法在背景線程執行,完成任務的主要工作,通常需要較長的時間。在執行過程中可以調用publicProgress(Progress…)來更新任務的進度。執行中遇到異常 怎麼通知呢? 支援(0) 反對(0)    #6樓   2013-04-27 15:23 |  燊仔   

@singlecheng

上面的還不是源碼嗎 支援(0) 反對(0)    #7樓   2013-06-12 21:33 |  draem0507   

@nickycookie

引用 publicProgress好像應該是publishProgress

好眼力 支援(0) 反對(0)    #8樓   2013-11-21 15:49 |  youJumpILook   

我見過的最适合新手入門的例子 支援(0) 反對(0)    #9樓   2014-02-28 10:56 |  十旋轉45度   

謝謝分享,講解簡單明了 支援(0) 反對(0)    #10樓   2014-03-06 16:40 |  沉默年代   

簡單明了,好好好 支援(0) 反對(0)    #11樓   2014-04-14 16:00 |  jiangyi355   

樓主,如果沒有參數要帶入呢 支援(0) 反對(0)    #12樓   2014-06-17 11:38 |  shashashashi   

很好的講解。 支援(0) 反對(0)    #13樓   2014-12-08 23:42 |  長風無及   

good article! 支援(0) 反對(0)    #14樓   2014-12-15 21:56 |  想飛的更遠   

你好,部落客,你是怎麼知道進度是

int vlaue = values[0]; 

progressBar.setProgress(vlaue); 

的呢?這個values[0]是什麼意義呢? 支援(0) 反對(0)    #15樓   2015-02-09 10:23 |  修雨軒陳   

@renfujiang

在onProgressUpdate()裡面的progressBar.setProgress(values[0]);, doInBackground()中調用了這個方法 , 支援(0) 反對(0)    #16樓   2015-02-09 10:24 |  修雨軒陳   

@想飛的更遠

引用 你好,部落客,你是怎麼知道進度是

int vlaue = values[0]; 

progressBar.setProgress(vlaue); 

的呢?這個values[0]是什麼意義呢?

Integer ...value是不能确定數量的參數 , java的main(String[] args)也是ruc , 是以第一個參數就是 下标為0的參數 支援(0) 反對(0)    #17樓   2015-03-17 15:23 |  生來笨鳥   

@想飛的更遠

通過doInBackground(Integer... params) 裡調用的publishProgress(Integer... params)方法,把進度發送到onProgressUpdate()裡 支援(0) 反對(0)    #18樓   2015-09-17 16:46 |  聞香識好   

怒關注一記。 支援(0) 反對(0)    #19樓   2015-11-13 14:50 |  林姐姐   

感謝部落客~ 支援(0) 反對(0)    #20樓   2016-01-04 10:33 |  冬眠FF   

謝謝部落客! 支援(0) 反對(0)    #21樓   2016-02-19 15:41 |  蕭穎儇   

寫的很好~适合入門學習~多謝部落客共享 支援(0) 反對(0)    #22樓   2016-03-08 19:36 |  zzqhfuf   

簡單明了,樓主好樣的

詳解Android中AsyncTask的使用

标簽: android任務layoutbuttonthreadasynchronous 2011-06-08 19:00  182398人閱讀  評論(116)  收藏  舉報

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

  分類:   Android(31) 

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。

在Android中實作異步任務機制有兩種方式,Handler和AsyncTask。

Handler模式需要為每一個任務建立一個新的線程,任務完成後通過Handler執行個體向UI線程發送消息,完成界面的更新,這種方式對于整個過程的控制比較精細,但也是有缺點的,例如代碼相對臃腫,在多個任務同時執行時,不易對線程進行精确的控制。關于Handler的相關知識,前面也有所介紹,不清楚的朋友們可以參照一下。

為了簡化操作,Android1.5提供了工具類android.os.AsyncTask,它使建立異步任務變得更加簡單,不再需要編寫任務線程和Handler執行個體即可完成相同的任務。

先來看看AsyncTask的定義:

[java]  view plain  copy

  1. public abstract class AsyncTask<Params, Progress, Result> {  

三種泛型類型分别代表“啟動任務執行的輸入參數”、“背景任務執行的進度”、“背景計算結果的類型”。在特定場合下,并不是所有類型都被使用,如果沒有被使用,可以用java.lang.Void類型代替。

一個異步任務的執行一般包括以下幾個步驟:

1.execute(Params... params),執行一個異步任務,需要我們在代碼中調用此方法,觸發異步任務的執行。

2.onPreExecute(),在execute(Params... params)被調用後立即執行,一般用來在執行背景任務前對UI做一些标記。

3.doInBackground(Params... params),在onPreExecute()完成後立即執行,用于執行較為費時的操作,此方法将接收輸入參數和傳回計算結果。在執行過程中可以調用publishProgress(Progress... values)來更新進度資訊。

4.onProgressUpdate(Progress... values),在調用publishProgress(Progress... values)時,此方法被執行,直接将進度資訊更新到UI元件上。

5.onPostExecute(Result result),當背景操作結束時,此方法将會被調用,計算結果将做為參數傳遞到此方法中,直接将結果顯示到UI元件上。

在使用的時候,有幾點需要格外注意:

1.異步任務的執行個體必須在UI線程中建立。

2.execute(Params... params)方法必須在UI線程中調用。

3.不要手動調用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。

4.不能在doInBackground(Params... params)中更改UI元件的資訊。

5.一個任務執行個體隻能執行一次,如果執行第二次将會抛出異常。

接下來,我們來看看如何使用AsyncTask執行異步任務操作,我們先建立一個項目,結構如下:

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

結構相對簡單一些,讓我們先看看MainActivity.java的代碼:

[java]  view plain  copy

  1. package com.scott.async;  
  2. import java.io.ByteArrayOutputStream;  
  3. import java.io.InputStream;  
  4. import org.apache.http.HttpEntity;  
  5. import org.apache.http.HttpResponse;  
  6. import org.apache.http.HttpStatus;  
  7. import org.apache.http.client.HttpClient;  
  8. import org.apache.http.client.methods.HttpGet;  
  9. import org.apache.http.impl.client.DefaultHttpClient;  
  10. import android.app.Activity;  
  11. import android.os.AsyncTask;  
  12. import android.os.Bundle;  
  13. import android.util.Log;  
  14. import android.view.View;  
  15. import android.widget.Button;  
  16. import android.widget.ProgressBar;  
  17. import android.widget.TextView;  
  18. public class MainActivity extends Activity {  
  19.     private static final String TAG = "ASYNC_TASK";  
  20.     private Button execute;  
  21.     private Button cancel;  
  22.     private ProgressBar progressBar;  
  23.     private TextView textView;  
  24.     private MyTask mTask;  
  25.     @Override  
  26.     public void onCreate(Bundle savedInstanceState) {  
  27.         super.onCreate(savedInstanceState);  
  28.         setContentView(R.layout.main);  
  29.         execute = (Button) findViewById(R.id.execute);  
  30.         execute.setOnClickListener(new View.OnClickListener() {  
  31.             @Override  
  32.             public void onClick(View v) {  
  33.                 //注意每次需new一個執行個體,建立的任務隻能執行一次,否則會出現異常  
  34.                 mTask = new MyTask();  
  35.                 mTask.execute("http://www.baidu.com");  
  36.                 execute.setEnabled(false);  
  37.                 cancel.setEnabled(true);  
  38.             }  
  39.         });  
  40.         cancel = (Button) findViewById(R.id.cancel);  
  41.         cancel.setOnClickListener(new View.OnClickListener() {  
  42.             @Override  
  43.             public void onClick(View v) {  
  44.                 //取消一個正在執行的任務,onCancelled方法将會被調用  
  45.                 mTask.cancel(true);  
  46.             }  
  47.         });  
  48.         progressBar = (ProgressBar) findViewById(R.id.progress_bar);  
  49.         textView = (TextView) findViewById(R.id.text_view);  
  50.     }  
  51.     private class MyTask extends AsyncTask<String, Integer, String> {  
  52.         //onPreExecute方法用于在執行背景任務前做一些UI操作  
  53.         @Override  
  54.         protected void onPreExecute() {  
  55.             Log.i(TAG, "onPreExecute() called");  
  56.             textView.setText("loading...");  
  57.         }  
  58.         //doInBackground方法内部執行背景任務,不可在此方法内修改UI  
  59.         @Override  
  60.         protected String doInBackground(String... params) {  
  61.             Log.i(TAG, "doInBackground(Params... params) called");  
  62.             try {  
  63.                 HttpClient client = new DefaultHttpClient();  
  64.                 HttpGet get = new HttpGet(params[0]);  
  65.                 HttpResponse response = client.execute(get);  
  66.                 if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {  
  67.                     HttpEntity entity = response.getEntity();  
  68.                     InputStream is = entity.getContent();  
  69.                     long total = entity.getContentLength();  
  70.                     ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  71.                     byte[] buf = new byte[1024];  
  72.                     int count = 0;  
  73.                     int length = -1;  
  74.                     while ((length = is.read(buf)) != -1) {  
  75.                         baos.write(buf, 0, length);  
  76.                         count += length;  
  77.                         //調用publishProgress公布進度,最後onProgressUpdate方法将被執行  
  78.                         publishProgress((int) ((count / (float) total) * 100));  
  79.                         //為了示範進度,休眠500毫秒  
  80.                         Thread.sleep(500);  
  81.                     }  
  82.                     return new String(baos.toByteArray(), "gb2312");  
  83.                 }  
  84.             } catch (Exception e) {  
  85.                 Log.e(TAG, e.getMessage());  
  86.             }  
  87.             return null;  
  88.         }  
  89.         //onProgressUpdate方法用于更新進度資訊  
  90.         @Override  
  91.         protected void onProgressUpdate(Integer... progresses) {  
  92.             Log.i(TAG, "onProgressUpdate(Progress... progresses) called");  
  93.             progressBar.setProgress(progresses[0]);  
  94.             textView.setText("loading..." + progresses[0] + "%");  
  95.         }  
  96.         //onPostExecute方法用于在執行完背景任務後更新UI,顯示結果  
  97.         @Override  
  98.         protected void onPostExecute(String result) {  
  99.             Log.i(TAG, "onPostExecute(Result result) called");  
  100.             textView.setText(result);  
  101.             execute.setEnabled(true);  
  102.             cancel.setEnabled(false);  
  103.         }  
  104.         //onCancelled方法用于在取消執行中的任務時更改UI  
  105.         @Override  
  106.         protected void onCancelled() {  
  107.             Log.i(TAG, "onCancelled() called");  
  108.             textView.setText("cancelled");  
  109.             progressBar.setProgress(0);  
  110.             execute.setEnabled(true);  
  111.             cancel.setEnabled(false);  
  112.         }  
  113.     }  
  114. }  

布局檔案main.xml代碼如下:

[xhtml]  view plain  copy

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent">  
  6.     <Button  
  7.         android:id="@+id/execute"  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="execute"/>  
  11.     <Button  
  12.         android:id="@+id/cancel"  
  13.         android:layout_width="fill_parent"  
  14.         android:layout_height="wrap_content"  
  15.         android:enabled="false"  
  16.         android:text="cancel"/>  
  17.     <ProgressBar   
  18.         android:id="@+id/progress_bar"   
  19.         android:layout_width="fill_parent"   
  20.         android:layout_height="wrap_content"   
  21.         android:progress="0"  
  22.         android:max="100"  
  23.         style="?android:attr/progressBarStyleHorizontal"/>  
  24.     <ScrollView  
  25.         android:layout_width="fill_parent"   
  26.         android:layout_height="wrap_content">  
  27.         <TextView  
  28.             android:id="@+id/text_view"  
  29.             android:layout_width="fill_parent"   
  30.             android:layout_height="wrap_content"/>  
  31.     </ScrollView>  
  32. </LinearLayout>  

因為需要通路網絡,是以我們還需要在AndroidManifest.xml中加入通路網絡的權限:

[xhtml]  view plain  copy

  1. <uses-permission android:name="android.permission.INTERNET"/>  

我們來看一下運作時的界面:

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔
android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔
android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔
android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

以上幾個截圖分别是初始界面、執行異步任務時界面、執行成功後界面、取消任務後界面。執行成功後,整個過程日志列印如下:

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

如果我們在執行任務時按下了“cancel”按鈕,日志列印如下:

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

可以看到onCancelled()方法将會被調用,onPostExecute(Result result)方法将不再被調用。

上面介紹了AsyncTask的基本應用,有些朋友也許會有疑惑,AsyncTask内部是怎麼執行的呢,它執行的過程跟我們使用Handler又有什麼差別呢?答案是:AsyncTask是對Thread+Handler良好的封裝,在android.os.AsyncTask代碼裡仍然可以看到Thread和Handler的蹤迹。下面就向大家詳細介紹一下AsyncTask的執行原理。

我們先看一下AsyncTask的大綱視圖:

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

我們可以看到關鍵幾個步驟的方法都在其中,doInBackground(Params... params)是一個抽象方法,我們繼承AsyncTask時必須覆寫此方法;onPreExecute()、onProgressUpdate(Progress... values)、onPostExecute(Result result)、onCancelled()這幾個方法體都是空的,我們需要的時候可以選擇性的覆寫它們;publishProgress(Progress... values)是final修飾的,不能覆寫,隻能去調用,我們一般會在doInBackground(Params... params)中調用此方法;另外,我們可以看到有一個Status的枚舉類和getStatus()方法,Status枚舉類代碼段如下:

[java]  view plain  copy

  1.         //初始狀态  
  2.     private volatile Status mStatus = Status.PENDING;  
  3.     public enum Status {  
  4.         PENDING,  
  5.         RUNNING,  
  6.         FINISHED,  
  7.     }  
  8.     public final Status getStatus() {  
  9.         return mStatus;  
  10.     }  

可以看到,AsyncTask的初始狀态為PENDING,代表待定狀态,RUNNING代表執行狀态,FINISHED代表結束狀态,這幾種狀态在AsyncTask一次生命周期内的很多地方被使用,非常重要。

介紹完大綱視圖相關内容之後,接下來,我們會從execute(Params... params)作為入口,重點分析一下AsyncTask的執行流程,我們來看一下execute(Params... params)方法的代碼段:

[java]  view plain  copy

  1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
  2.         if (mStatus != Status.PENDING) {  
  3.             switch (mStatus) {  
  4.                 case RUNNING:  
  5.                     //如果該任務正在被執行則抛出異常  
  6.                     //值得一提的是,在調用cancel取消任務後,狀态仍未RUNNING  
  7.                     throw new IllegalStateException("Cannot execute task:"  
  8.                             + " the task is already running.");  
  9.                 case FINISHED:  
  10.                     //如果該任務已經執行完成則抛出異常  
  11.                     throw new IllegalStateException("Cannot execute task:"  
  12.                             + " the task has already been executed "  
  13.                             + "(a task can be executed only once)");  
  14.             }  
  15.         }  
  16.         //改變狀态為RUNNING  
  17.         mStatus = Status.RUNNING;  
  18.         //調用onPreExecute方法  
  19.         onPreExecute();  
  20.         mWorker.mParams = params;  
  21.         sExecutor.execute(mFuture);  
  22.         return this;  
  23.     }  

代碼中涉及到三個陌生的變量:mWorker、sExecutor、mFuture,我們也會看一下他們的廬山真面目:

關于sExecutor,它是java.util.concurrent.ThreadPoolExecutor的執行個體,用于管理線程的執行。代碼如下:

[java]  view plain  copy

  1. private static final int CORE_POOL_SIZE = 5;  
  2.    private static final int MAXIMUM_POOL_SIZE = 128;  
  3.    private static final int KEEP_ALIVE = 10;  
  4. //建立一個隊列用來存放線程  
  5.    private static final BlockingQueue<Runnable> sWorkQueue =  
  6.            new LinkedBlockingQueue<Runnable>(10);  
  7. //建立一個線程工廠  
  8.    private static final ThreadFactory sThreadFactory = new ThreadFactory() {  
  9.        private final AtomicInteger mCount = new AtomicInteger(1);  
  10.     //建立一個線程  
  11.        public Thread newThread(Runnable r) {  
  12.            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());  
  13.        }  
  14.    };  
  15. //建立一個線程池執行器,用于管理線程的執行  
  16.    private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
  17.            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);  

mWorker實際上是AsyncTask的一個的抽象内部類的實作對象執行個體,它實作了Callable<Result>接口中的call()方法,代碼如下:

[java]  view plain  copy

  1. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
  2.         Params[] mParams;  
  3.     }  

而mFuture實際上是java.util.concurrent.FutureTask的執行個體,下面是它的FutureTask類的相關資訊:

[java]  view plain  copy

  1. public class FutureTask<V> implements RunnableFuture<V> {  

[java]  view plain  copy

  1. public interface RunnableFuture<V> extends Runnable, Future<V> {  
  2.     void run();  
  3. }  

可以看到FutureTask是一個可以中途取消的用于異步計算的類。

下面是mWorker和mFuture執行個體在AsyncTask中的展現:

[java]  view plain  copy

  1.    private final WorkerRunnable<Params, Result> mWorker;  
  2.    private final FutureTask<Result> mFuture;  
  3. public AsyncTask() {  
  4.        mWorker = new WorkerRunnable<Params, Result>() {  
  5.            //call方法被調用後,将設定優先級為背景級别,然後調用AsyncTask的doInBackground方法  
  6.         public Result call() throws Exception {  
  7.                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  8.                return doInBackground(mParams);  
  9.            }  
  10.        };  
  11.     //在mFuture執行個體中,将會調用mWorker做背景任務,完成後會調用done方法  
  12.        mFuture = new FutureTask<Result>(mWorker) {  
  13.            @Override  
  14.            protected void done() {  
  15.                Message message;  
  16.                Result result = null;  
  17.                try {  
  18.                    result = get();  
  19.                } catch (InterruptedException e) {  
  20.                    android.util.Log.w(LOG_TAG, e);  
  21.                } catch (ExecutionException e) {  
  22.                    throw new RuntimeException("An error occured while executing doInBackground()",  
  23.                            e.getCause());  
  24.                } catch (CancellationException e) {  
  25.                 //發送取消任務的消息  
  26.                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
  27.                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));  
  28.                    message.sendToTarget();  
  29.                    return;  
  30.                } catch (Throwable t) {  
  31.                    throw new RuntimeException("An error occured while executing "  
  32.                            + "doInBackground()", t);  
  33.                }  
  34.             //發送顯示結果的消息  
  35.                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
  36.                        new AsyncTaskResult<Result>(AsyncTask.this, result));  
  37.                message.sendToTarget();  
  38.            }  
  39.        };  
  40.    }  

 我們看到上面的代碼中,mFuture執行個體對象的done()方法中,如果捕捉到了CancellationException類型的異常,則發送一條“MESSAGE_POST_CANCEL”的消息;如果順利執行,則發送一條“MESSAGE_POST_RESULT”的消息,而消息都與一個sHandler對象關聯。這個sHandler執行個體實際上是AsyncTask内部類InternalHandler的執行個體,而InternalHandler正是繼承了Handler,下面我們來分析一下它的代碼:

[java]  view plain  copy

  1. private static final int MESSAGE_POST_RESULT = 0x1; //顯示結果  
  2.    private static final int MESSAGE_POST_PROGRESS = 0x2;    //更新進度  
  3.    private static final int MESSAGE_POST_CANCEL = 0x3;  //取消任務  
  4.    private static final InternalHandler sHandler = new InternalHandler();  
  5. private static class InternalHandler extends Handler {  
  6.        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})  
  7.        @Override  
  8.        public void handleMessage(Message msg) {  
  9.            AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
  10.            switch (msg.what) {  
  11.                case MESSAGE_POST_RESULT:  
  12.                    // There is only one result  
  13.                 //調用AsyncTask.finish方法  
  14.                    result.mTask.finish(result.mData[0]);  
  15.                    break;  
  16.                case MESSAGE_POST_PROGRESS:  
  17.                    //調用AsyncTask.onProgressUpdate方法  
  18.                 result.mTask.onProgressUpdate(result.mData);  
  19.                    break;  
  20.                case MESSAGE_POST_CANCEL:  
  21.                 //調用AsyncTask.onCancelled方法  
  22.                    result.mTask.onCancelled();  
  23.                    break;  
  24.            }  
  25.        }  
  26.    }  

我們看到,在處理消息時,遇到“MESSAGE_POST_RESULT”時,它會調用AsyncTask中的finish()方法,我們來看一下finish()方法的定義:

[java]  view plain  copy

  1. private void finish(Result result) {  
  2.         if (isCancelled()) result = null;  
  3.         onPostExecute(result);  //調用onPostExecute顯示結果  
  4.         mStatus = Status.FINISHED;  //改變狀态為FINISHED  
  5.     }  

原來finish()方法是負責調用onPostExecute(Result result)方法顯示結果并改變任務狀态的啊。

另外,在mFuture對象的done()方法裡,建構一個消息時,這個消息包含了一個AsyncTaskResult類型的對象,然後在sHandler執行個體對象的handleMessage(Message msg)方法裡,使用下面這種方式取得消息中附帶的對象:

[java]  view plain  copy

  1. AsyncTaskResult result = (AsyncTaskResult) msg.obj;  

這個AsyncTaskResult究竟是什麼呢,它又包含什麼内容呢?其實它也是AsyncTask的一個内部類,是用來包裝執行結果的一個類,讓我們來看一下它的代碼結構:

[java]  view plain  copy

  1. @SuppressWarnings({"RawUseOfParameterizedType"})  
  2. private static class AsyncTaskResult<Data> {  
  3.     final AsyncTask mTask;  
  4.     final Data[] mData;  
  5.     AsyncTaskResult(AsyncTask task, Data... data) {  
  6.         mTask = task;  
  7.         mData = data;  
  8.     }  
  9. }  

看以看到這個AsyncTaskResult封裝了一個AsyncTask的執行個體和某種類型的資料集,我們再來看一下建構消息時的代碼:

[java]  view plain  copy

  1. //發送取消任務的消息  
  2. message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
  3.         new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));  
  4. message.sendToTarget();  

[java]  view plain  copy

  1. //發送顯示結果的消息  
  2. message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
  3.          new AsyncTaskResult<Result>(AsyncTask.this, result));  
  4. message.sendToTarget();  

在處理消息時是如何使用這個對象呢,我們再來看一下:

[java]  view plain  copy

  1. result.mTask.finish(result.mData[0]);  

[java]  view plain  copy

  1. result.mTask.onProgressUpdate(result.mData);  

概括來說,當我們調用execute(Params... params)方法後,execute方法會調用onPreExecute()方法,然後由ThreadPoolExecutor執行個體sExecutor執行一個FutureTask任務,這個過程中doInBackground(Params... params)将被調用,如果被開發者覆寫的doInBackground(Params... params)方法中調用了publishProgress(Progress... values)方法,則通過InternalHandler執行個體sHandler發送一條MESSAGE_POST_PROGRESS消息,更新進度,sHandler處理消息時onProgressUpdate(Progress... values)方法将被調用;如果遇到異常,則發送一條MESSAGE_POST_CANCEL的消息,取消任務,sHandler處理消息時onCancelled()方法将被調用;如果執行成功,則發送一條MESSAGE_POST_RESULT的消息,顯示結果,sHandler處理消息時onPostExecute(Result result)方法被調用。

經過上面的介紹,相信朋友們都已經認識到AsyncTask的本質了,它對Thread+Handler的良好封裝,減少了開發者處理問題的複雜度,提高了開發效率,希望朋友們能多多體會一下。

25
  • 上一篇詳解Android首選項架構的使用
  • 下一篇淺談WebView的使用

[Android] AsyncTask使用執行個體---加載網絡圖檔

标簽: android網絡floatstringnullui 2012-07-12 18:46  5445人閱讀  評論(4)  收藏  舉報

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

  分類:   Android(47) 

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。

先上效果圖。如demo_asynctask.gif

android AsyncTask介紹 詳解AsyncTask的使用 使用執行個體---加載網絡圖檔android AsyncTask介紹 詳解Android中AsyncTask的使用 [Android] AsyncTask使用執行個體---加載網絡圖檔

對于圖檔的加載效果,見連結:[Android] PorterDuff使用執行個體----實作新浪微網誌圖檔下載下傳效果

本文參考連結:http://developer.android.com/reference/android/os/AsyncTask.html

AsyncTask被設計成友善編寫Thread與Handler互動的輔助類,幾秒鐘的背景耗時操作是理想的使用場合。

AsyncTask必須被子類化才能使用,在該過程中必須設定3個構造參數:

           1.Params 往背景線程執行時的參數類型

           2.Progress 背景線程釋出任務進度的參數類型

           3.Result 背景線程的運作後最後傳回的結果的參數類型

如果不需要聲明參數類型,可以使用"Void"進行替代。

通常子類化聲明代碼:

[java]  view plain  copy

  1. public class DownloadImgTask extends AsyncTask<String, Float, Bitmap>  

不需要參數的聲明代碼:

[java]  view plain  copy

  1. public class DownloadImgTask extends AsyncTask<Void, Void, Void>  

當一個AsyncTask被執行後,會有如下4個步驟:

           1.onPreExecute() 在UI線程中調用。這個步驟通常用于一些準備操作,如顯示一個進度條

           2.doInBackground(Params ...) 當onPreExecute()執行完畢後,即被背景線程調用執行。這個步驟用于執行耗時的計算。方法中為不定參數,由外部調用AsyncTask.execute(Params...)時設定。本步驟運作的過程可以通過publishProgress(Progress...)釋出到onProgressUpdate(Progress...)中,運作結果将return,後被傳參至onPostExecute(Result)。

           3.onProgressUpdate(Progress...) 當執行publishProgress(Progress...)後在UI線程中被調用。執行的時間是不确定的。

           4.onPostExecute(Result) 在UI線程中被調用。參數為doInBackground(Params...)的運作結果。

在AsyncTask的執行過程中可以調用cancel(boolean),方法中的參數值為false時允許已經開始工作的背景線程繼續工作至任務完成;為true時則強制停止。AsyncTask将不再調用onPostExecute(Result),取而代之的是調用onCancelled(Object)。為了确定目前的AsyncTask是否已經被cancelled,應該在doInBackground(Params...)方法中執行isCancelled()進行檢查。

為了AsyncTask順利工作,必須遵守線程規則:

           1.AsyncTask必須在UI線程中被調用。JELLY_BEAN版本預設已經遵守此規則。

           2.AsyncTask的執行個體必須在UI線程中被建立。

           3.execute(Params...)必須在UI線程中被調用。

           4.不允許手工調用以下方法:

                      onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)

           5.AsyncTask的執行個體隻能被執行一次。再次執行時将會抛出異常:

                      java.lang.IllegalStateException: Cannot execute task: the task is already running.

本文為Sodino所有,轉載請注明出處:http://blog.csdn.net/sodino/article/details/7741674

Java代碼貼上,XML請各位看官自行實作:

[java]  view plain  copy

  1. ActAsyncTask.java  
  2. package lab.sodino.asynctask;  
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.view.View;  
  6. import android.view.View.OnClickListener;  
  7. public class ActAsyncTask extends Activity implements OnClickListener {  
  8.     private PorterDuffView pViewA, pViewB, pViewC, pViewD;  
  9.     public static final String[] STRING_ARR = {//  
  10.     "http://developer.android.com/images/home/android-jellybean.png",//  
  11.             "http://developer.android.com/images/home/design.png",//  
  12.             "http://developer.android.com/images/home/google-play.png",//  
  13.             "http://developer.android.com/images/home/google-io.png" };  
  14.     public void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.main);  
  17.         pViewA = (PorterDuffView) findViewById(R.id.pViewA);  
  18.         pViewA.setOnClickListener(this);  
  19.         pViewB = (PorterDuffView) findViewById(R.id.pViewB);  
  20.         pViewB.setOnClickListener(this);  
  21.         pViewC = (PorterDuffView) findViewById(R.id.pViewC);  
  22.         pViewC.setOnClickListener(this);  
  23.         pViewD = (PorterDuffView) findViewById(R.id.pViewD);  
  24.         pViewD.setOnClickListener(this);  
  25.     }  
  26.     public void onClick(View v) {  
  27.         if (v instanceof PorterDuffView) {  
  28.             PorterDuffView pdView = (PorterDuffView) v;  
  29.             if (pdView.isLoading() == false) {  
  30.                 DownloadImgTask task = new DownloadImgTask(pdView);  
  31.                 task.execute(STRING_ARR[pdView.getId() % STRING_ARR.length]);  
  32.                 pdView.setPorterDuffMode(true);  
  33.                 pdView.setLoading(true);  
  34.                 pdView.setProgress(0);  
  35.                 pdView.invalidate();  
  36.             }  
  37.         }  
  38.     }  
  39. }  

[java]  view plain  copy

  1. DownloadImgTask.java  
  2. package lab.sodino.asynctask;  
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import org.apache.http.Header;  
  7. import org.apache.http.HttpEntity;  
  8. import org.apache.http.HttpResponse;  
  9. import org.apache.http.client.ClientProtocolException;  
  10. import org.apache.http.client.HttpClient;  
  11. import org.apache.http.client.methods.HttpGet;  
  12. import org.apache.http.impl.client.DefaultHttpClient;  
  13. import org.apache.http.params.HttpParams;  
  14. import android.graphics.Bitmap;  
  15. import android.graphics.BitmapFactory;  
  16. import android.os.AsyncTask;  
  17. public class DownloadImgTask extends AsyncTask<String, Float, Bitmap> {  
  18.     private PorterDuffView pdView;  
  19.     public DownloadImgTask(PorterDuffView pdView) {  
  20.         this.pdView = pdView;  
  21.     }  
  22.     protected void onPreExecute() {  
  23.         LogOut.out(this, "onPreExecute");  
  24.     }  
  25.     protected Bitmap doInBackground(String... params) {  
  26.         LogOut.out(this, "doInBackground:" + params[0]);  
  27.         HttpClient httpClient = new DefaultHttpClient();  
  28.         HttpGet httpGet = new HttpGet(params[0]);  
  29.         InputStream is = null;  
  30.         ByteArrayOutputStream baos = null;  
  31.         try {  
  32.             HttpResponse httpResponse = httpClient.execute(httpGet);  
  33.             printHttpResponse(httpResponse);  
  34.             HttpEntity httpEntity = httpResponse.getEntity();  
  35.             long length = httpEntity.getContentLength();  
  36.             LogOut.out(this, "content length=" + length);  
  37.             is = httpEntity.getContent();  
  38.             if (is != null) {  
  39.                 baos = new ByteArrayOutputStream();  
  40.                 byte[] buf = new byte[128];  
  41.                 int read = -1;  
  42.                 int count = 0;  
  43.                 while ((read = is.read(buf)) != -1) {  
  44.                     baos.write(buf, 0, read);  
  45.                     count += read;  
  46.                     publishProgress(count * 1.0f / length);  
  47.                 }  
  48.                 LogOut.out(this, "count=" + count + " length=" + length);  
  49.                 byte[] data = baos.toByteArray();  
  50.                 Bitmap bit = BitmapFactory.decodeByteArray(data, 0, data.length);  
  51.                 return bit;  
  52.             }  
  53.         } catch (ClientProtocolException e) {  
  54.             e.printStackTrace();  
  55.         } catch (IOException e) {  
  56.             e.printStackTrace();  
  57.         } finally {  
  58.             try {  
  59.                 if (baos != null) {  
  60.                     baos.close();  
  61.                 }  
  62.                 if (is != null) {  
  63.                     is.close();  
  64.                 }  
  65.             } catch (IOException e) {  
  66.                 e.printStackTrace();  
  67.             }  
  68.         }  
  69.         return null;  
  70.     }  
  71.     protected void onProgressUpdate(Float... progress) {  
  72.         // LogOut.out(this, "onProgressUpdate");  
  73.         pdView.setProgress(progress[0]);  
  74.     }  
  75.     protected void onPostExecute(Bitmap bit) {  
  76.         LogOut.out(this, "onPostExecute");  
  77.         pdView.setPorterDuffMode(false);  
  78.         pdView.setLoading(false);  
  79.         pdView.setImageBitmap(bit);  
  80.     }  
  81.     protected void onCancelled() {  
  82.         LogOut.out(this, "DownloadImgTask cancel...");  
  83.         super.onCancelled();  
  84.     }  
  85.     private void printHttpResponse(HttpResponse httpResponse) {  
  86.         Header[] headerArr = httpResponse.getAllHeaders();  
  87.         for (int i = 0; i < headerArr.length; i++) {  
  88.             Header header = headerArr[i];  
  89.             LogOut.out(this, "name[" + header.getName() + "]value[" + header.getValue() + "]");  
  90.         }  
  91.         HttpParams params = httpResponse.getParams();  
  92.         LogOut.out(this, String.valueOf(params));  
  93.         LogOut.out(this, String.valueOf(httpResponse.getLocale()));  
  94.     }  
  95. }  

[java]  view plain  copy

  1. PorterDuffView.java  
  2. package lab.sodino.asynctask;  
  3. import java.text.DecimalFormat;  
  4. import android.content.Context;  
  5. import android.content.res.TypedArray;  
  6. import android.graphics.Bitmap;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Paint;  
  9. import android.graphics.PorterDuff;  
  10. import android.graphics.PorterDuffXfermode;  
  11. import android.graphics.drawable.BitmapDrawable;  
  12. import android.graphics.drawable.Drawable;  
  13. import android.util.AttributeSet;  
  14. import android.widget.ImageView;  
  15. public class PorterDuffView extends ImageView {  
  16.     public static final int FG_HEIGHT = 1;  
  17.     // public static final int FOREGROUND_COLOR = 0x77123456;  
  18.     public static final int FOREGROUND_COLOR = 0x77ff0000;  
  19.     public static final int TEXT_COLOR = 0xff7fff00;  
  20.     public static final int FONT_SIZE = 30;  
  21.     private Bitmap bitmapBg, bitmapFg;  
  22.     private Paint paint;  
  23.     private float progress;  
  24.     private int width, height;  
  25.     private DecimalFormat decFormat;  
  26.     private float txtBaseY;  
  27.     private boolean porterduffMode;  
  28.     private boolean loading;  
  29.     public PorterDuffView(Context context, AttributeSet attrs) {  
  30.         super(context, attrs);  
  31.         init(context, attrs);  
  32.     }  
  33.     private static Bitmap createForegroundBitmap(int w) {  
  34.         Bitmap bm = Bitmap.createBitmap(w, FG_HEIGHT, Bitmap.Config.ARGB_8888);  
  35.         Canvas c = new Canvas(bm);  
  36.         Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);  
  37.         p.setColor(FOREGROUND_COLOR);  
  38.         c.drawRect(0, 0, w, FG_HEIGHT, p);  
  39.         return bm;  
  40.     }  
  41.     private void init(Context context, AttributeSet attrs) {  
  42.         if (attrs != null) {  
  43.             // //  
  44.             // int count = attrs.getAttributeCount();  
  45.             // for (int i = 0; i < count; i++) {  
  46.             // LogOut.out(this, "attrNameRes:" +  
  47.             // Integer.toHexString(attrs.getAttributeNameResource(i))//  
  48.             // + " attrName:" + attrs.getAttributeName(i)//  
  49.             // + " attrResValue:" + attrs.getAttributeResourceValue(i, -1)//  
  50.             // + " attrValue:" + attrs.getAttributeValue(i)//  
  51.             // );  
  52.             // }  
  53.             // //  
  54.             TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.porterduff_PorterDuffView);  
  55.             porterduffMode = typedArr.getBoolean(R.styleable.porterduff_PorterDuffView_porterduffMode, false);  
  56.         }  
  57.         Drawable drawable = getDrawable();  
  58.         if (porterduffMode && drawable != null && drawable instanceof BitmapDrawable) {  
  59.             bitmapBg = ((BitmapDrawable) drawable).getBitmap();  
  60.             width = bitmapBg.getWidth();  
  61.             height = bitmapBg.getHeight();  
  62.             // LogOut.out(this, "width=" + width + " height=" + height);  
  63.             bitmapFg = createForegroundBitmap(width);  
  64.         } else {  
  65.             // 不符合要求,自動設定為false。  
  66.             porterduffMode = false;  
  67.         }  
  68.         paint = new Paint();  
  69.         paint.setFilterBitmap(false);  
  70.         paint.setAntiAlias(true);  
  71.         paint.setTextSize(FONT_SIZE);  
  72.         // 關于FontMetrics的詳情介紹,可見:  
  73.         // http://xxxxxfsadf.iteye.com/blog/480454  
  74.         Paint.FontMetrics fontMetrics = paint.getFontMetrics();  
  75.         // 注意觀察本輸出:  
  76.         // ascent:單個字元基線以上的推薦間距,為負數  
  77.         LogOut.out(this, "ascent:" + fontMetrics.ascent//  
  78.                 // descent:單個字元基線以下的推薦間距,為正數  
  79.                 + " descent:" + fontMetrics.descent //  
  80.                 // 單個字元基線以上的最大間距,為負數  
  81.                 + " top:" + fontMetrics.top //  
  82.                 // 單個字元基線以下的最大間距,為正數  
  83.                 + " bottom:" + fontMetrics.bottom//  
  84.                 // 文本行與行之間的推薦間距  
  85.                 + " leading:" + fontMetrics.leading);  
  86.         // 在此處直接計算出來,避免了在onDraw()處的重複計算  
  87.         txtBaseY = (height - fontMetrics.bottom - fontMetrics.top) / 2;  
  88.         decFormat = new DecimalFormat("0.0%");  
  89.     }  
  90.     public void onDraw(Canvas canvas) {  
  91.         if (porterduffMode) {  
  92.             int tmpW = (getWidth() - width) / 2, tmpH = (getHeight() - height) / 2;  
  93.             // 畫出背景圖  
  94.             canvas.drawBitmap(bitmapBg, tmpW, tmpH, paint);  
  95.             // 設定PorterDuff模式  
  96.             paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));  
  97.             // canvas.drawBitmap(bitmapFg, tmpW, tmpH - progress * height,  
  98.             // paint);  
  99.             int tH = height - (int) (progress * height);  
  100.             for (int i = 0; i < tH; i++) {  
  101.                 canvas.drawBitmap(bitmapFg, tmpW, tmpH + i, paint);  
  102.             }  
  103.             // 立即取消xfermode  
  104.             paint.setXfermode(null);  
  105.             int oriColor = paint.getColor();  
  106.             paint.setColor(TEXT_COLOR);  
  107.             paint.setTextSize(FONT_SIZE);  
  108.             String tmp = decFormat.format(progress);  
  109.             float tmpWidth = paint.measureText(tmp);  
  110.             canvas.drawText(decFormat.format(progress), tmpW + (width - tmpWidth) / 2, tmpH + txtBaseY, paint);  
  111.             // 恢複為初始值時的顔色  
  112.             paint.setColor(oriColor);  
  113.         } else {  
  114.             LogOut.out(this, "onDraw super");  
  115.             super.onDraw(canvas);  
  116.         }  
  117.     }  
  118.     public void setProgress(float progress) {  
  119.         if (porterduffMode) {  
  120.             this.progress = progress;  
  121.             // 重新整理自身。  
  122.             invalidate();  
  123.         }  
  124.     }  
  125.     public void setBitmap(Bitmap bg) {  
  126.         if (porterduffMode) {  
  127.             bitmapBg = bg;  
  128.             width = bitmapBg.getWidth();  
  129.             height = bitmapBg.getHeight();  
  130.             bitmapFg = createForegroundBitmap(width);  
  131.             Paint.FontMetrics fontMetrics = paint.getFontMetrics();  
  132.             txtBaseY = (height - fontMetrics.bottom - fontMetrics.top) / 2;  
  133.             setImageBitmap(bg);  
  134.             // 請求重新布局,将會再次調用onMeasure()  
  135.             // requestLayout();  
  136.         }  
  137.     }  
  138.     public boolean isLoading() {  
  139.         return loading;  
  140.     }  
  141.     public void setLoading(boolean loading) {  
  142.         this.loading = loading;  
  143.     }  
  144.     public void setPorterDuffMode(boolean bool) {  
  145.         porterduffMode = bool;  
  146.     }  
  147. }  
2
  • 上一篇[Android] PorterDuff使用執行個體----實作新浪微網誌圖檔下載下傳效果
  • 下一篇[Java] 生産者&消費者問題