標籤:
在《Android非同步處理一:使用Thread+Handler實現非UI線程更新UI介面》中,我們使用Thread+Handler的方式實現了非同步更新UI介面,這一篇中,我們介紹一種更為簡潔的實現方式:使用AsyncTask非同步更新UI介面。
概述: AsyncTask是在Android SDK 1.5之後推出的一個方便編寫後台線程與UI線程互動的輔助類。AsyncTask的內部實現是一個線程池,每個背景工作會提交到線程池中的線程執行,然後使用Thread+Handler的方式調用回呼函數(如需深入瞭解原理請看《Android非同步處理四:AsyncTask的實現原理》)。
AsyncTask抽象出後台線程啟動並執行五個狀態,分別是:1、準備運行,2、正在後台運行,3、進度更新,4、完成背景工作,5、取消任務,對於這五個階段,AsyncTask提供了五個回呼函數:
1、準備運行:onPreExecute(),該回呼函數在任務被執行之後立即由UI線程調用。這個步驟通常用來建立任務,在使用者介面(UI)上顯示進度條。
2、正在後台運行:doInBackground(Params...),該回呼函數由後台線程在onPreExecute()方法執行結束後立即調用。通常在這裡執行耗時的後台計算。計算的結果必須由該函數返回,並被傳遞到onPostExecute()中。在該函數內也可以使用publishProgress(Progress...)來發布一個或多個進度單位(unitsof progress)。這些值將會在onProgressUpdate(Progress...)中被發布到UI線程。
3. 進度更新:onProgressUpdate(Progress...),該函數由UI線程在publishProgress(Progress...)方法調用完後被調用。一般用於動態地顯示一個進度條。
4. 完成背景工作:onPostExecute(Result),當後台計算結束後調用。後台計算的結果會被作為參數傳遞給這一函數。
5、取消任務:onCancelled (),在調用AsyncTask的cancel()方法時調用
AsyncTask的建構函式有三個模板參數:
1.Params,傳遞給背景工作的參數類型。
2.Progress,後台計算執行過程中,進步單位(progress units)的類型。(就是背景程式已經執行了百分之幾了。)
3.Result, 後台執行返回的結果的類型。
AsyncTask並不總是需要使用上面的全部3種類型。標識不使用的類型很簡單,只需要使用Void類型即可。
例子:與《Android非同步處理一:使用Thread+Handler實現非UI線程更新UI介面》實現的例子相同,我們在後台下載CSDN的LOGO,下載完成後在UI介面上顯示出來,並會類比下載進度更新。
例子工程檔案下載
AsyncTaskActivity.java
package com.zhuozhuo;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ImageView;import android.widget.ProgressBar;import android.widget.Toast;public class AsyncTaskActivity extends Activity { private ImageView mImageView; private Button mButton; private ProgressBar mProgressBar; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mImageView= (ImageView) findViewById(R.id.imageView); mButton = (Button) findViewById(R.id.button); mProgressBar = (ProgressBar) findViewById(R.id.progressBar); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { GetCSDNLogoTask task = new GetCSDNLogoTask(); task.execute("http://csdnimg.cn/www/images/csdnindex_logo.gif"); } }); } class GetCSDNLogoTask extends AsyncTask<String,Integer,Bitmap> {//繼承AsyncTask @Override protected Bitmap doInBackground(String... params) {//處理後台執行的任務,在後台線程執行 publishProgress(0);//將會調用onProgressUpdate(Integer... progress)方法 HttpClient hc = new DefaultHttpClient(); publishProgress(30); HttpGet hg = new HttpGet(params[0]);//擷取csdn的logo final Bitmap bm; try { HttpResponse hr = hc.execute(hg); bm = BitmapFactory.decodeStream(hr.getEntity().getContent()); } catch (Exception e) { return null; } publishProgress(100); //mImageView.setImageBitmap(result); 不能在後台線程操作ui return bm; } protected void onProgressUpdate(Integer... progress) {//在調用publishProgress之後被調用,在ui線程執行 mProgressBar.setProgress(progress[0]);//更新進度條的進度 } protected void onPostExecute(Bitmap result) {//背景工作執行完之後被調用,在ui線程執行 if(result != null) { Toast.makeText(AsyncTaskActivity.this, "成功擷取圖片", Toast.LENGTH_LONG).show(); mImageView.setImageBitmap(result); }else { Toast.makeText(AsyncTaskActivity.this, "擷取圖片失敗", Toast.LENGTH_LONG).show(); } } protected void onPreExecute () {//在 doInBackground(Params...)之前被調用,在ui線程執行 mImageView.setImageBitmap(null); mProgressBar.setProgress(0);//進度條複位 } protected void onCancelled () {//在ui線程執行 mProgressBar.setProgress(0);//進度條複位 } } }
main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ProgressBar android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal"></ProgressBar> <Button android:id="@+id/button" android:text="下載圖片" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <ImageView android:id="@+id/imageView" android:layout_height="wrap_content" android:layout_width="wrap_content" /></LinearLayout>
manifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.zhuozhuo" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" /><uses-permission android:name="android.permission.INTERNET"></uses-permission> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".AsyncTaskActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
運行結果:
流程說明:
1、 當點擊按鈕時,建立一個task,並且傳入CSDN的LOGO地址(String型別參數,因此AsyncTask的第一個模板參數為String類型)
2、 UI線程執行onPreExecute(),把ImageView的圖片清空,progrssbar的進度清零。
3、 後台線程執行doInBackground(),不可以在doInBackground()操作ui,調用publishProgress(0)更新進度,此時會調用onProgressUpdate(Integer...progress)更新進度條(進度用整形表示,因此AsyncTask的第二個模板參數是Integer)。函數最後返回result(例子中是返回Bitmap類型,因此AsyncTask的第三個模板參數是Bitmap)。
4、 當背景工作執行完成後,調用onPostExecute(Result),傳入的參數是doInBackground()中返回的對象。
總結:
AsyncTask為我們抽象出一個背景工作的五種狀態,對應了五個回調介面,我們只需要根據不同的需求實現這五個介面(doInBackground是必須要實現的),就能完成一些簡單的背景工作。使用AsyncTask的方式使編寫後台進程和UI進程互動的代碼變得更為簡潔,使用起來更加方便,但是,AsyncTask也有一些缺憾,我們留到以後再講。
本博文地址:http://blog.csdn.net/mylzc/article/details/6772129,轉載請註明出處
Android非同步處理二:使用AsyncTask非同步更新UI介面