首先使用一個實例來解釋幾個異步之間的關係:
實例內容:從網絡上下載圖片
此實例由Sundy講解Android視頻提供的,地址:http://www.verycd.com/topics/2900036/
XML代碼:
Java代碼:
public class LoadImageTest extends Activity{
private static final String TAG = "LoadImageTest";
private ImageView mImageView = null ;
//從網絡上下載圖片 .
private final String IMAGE1_URL = "http://image.91rb.com/200905/27/9/34b5b080ac80661657946eaa51566d03.jpg" ;
private final String IMAGE3_URL = "http://m.ztwan.com/wallpaper/UploadPic/2010/8/28/201082811538894.jpg" ;
private final String IMAGE4_URL = "http://m.ztwan.com/wallpaper/UploadPic/2010/8/28/20108280578236.jpg" ;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.load_imagetest) ;
mImageView = (ImageView)this.findViewById(R.id.imageThreadConcept) ;
/方法1、直接在UI線程中加載網絡圖片
findViewById(R.id.buttonWorkThread).setOnClickListener(new OnClickListener(){
public void onClick(View v) {
// 這隻方式直接在網絡中得到一張圖片,因數據量比較小,在UI線程中執行,並不會造成用戶視覺上的等待,如果數據量龐大,不採用這中方式
Drawable drawable = loadImageFromNetwork(IMAGE1_URL);
mImageView.setImageDrawable(drawable) ;
}}) ;
/方法2、java習慣,在android不推薦使用,使得使用線程不安全
findViewById(R.id.buttonWorkThread2).setOnClickListener(new OnClickListener(){
public void onClick(View v) {
// 在Android中是灰常不建議這樣做的,這樣做極易出現異常
new Thread(new Runnable(){
public void run() {
Drawable drawable = loadImageFromNetwork(IMAGE1_URL);
mImageView.setImageDrawable(drawable) ;
}
}).start() ;
}}) ;
//3. load image in new thread , but set imageview by View.post(Runnable)
//方法3、創建一個新的線程,( Runnable + Handler.postDelayed(runnable,time) )
findViewById(R.id.buttonWorkThread3).setOnClickListener(new OnClickListener(){
public void onClick(View v) {
new Thread(new Runnable(){
Drawable drawable = loadImageFromNetwork(IMAGE3_URL);
public void run() {
mImageView.post(new Runnable(){
public void run() {
mImageView.setImageDrawable(drawable) ;
}}) ;
}
}).start() ;
}}) ;
//4. load image in new thread , but set imageview by AsyncTask
//方法4、使用AsyncTask,AsyncTask是在Android 1.5後引入的,能夠更安全的使用線程,在下面,將會再用一個實例來分析AsyncTask
findViewById(R.id.buttonWorkThread4).setOnClickListener(new OnClickListener(){
public void onClick(View v) {
// 不可缺少的異步,當數據量龐大時,耗時的操作時就使用這種方式吧。
//IMAGE4_URL 是執行傳入的參數
new DownloadImageTask().execute(IMAGE4_URL) ;
}}) ;
}
//Async private class
private class DownloadImageTask extends AsyncTask<String, Void, Drawable> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
protected Drawable doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
protected void onPostExecute(Drawable result) {
mImageView.setImageDrawable(result);
}
}
// the Drawable loadImage main function
private Drawable loadImageFromNetwork(String imageUrl)
{
Drawable drawable = null;
try {
drawable = Drawable.createFromStream(
new URL(imageUrl).openStream(), "image.gif");
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
if (drawable == null) {
Log.d(TAG, "null drawable");
} else {
Log.d(TAG, "not null drawable");
}
return drawable ;
}
}
在獨立線程中進行地理位置編碼:本實例來自於Pro Android2 精通Android 2
XML代碼:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<com.google.android.maps.MapView
android:id="@+id/geoMap" android:clickable="true"
android:layout_width="fill_parent" android:layout_height="450px"
android:apiKey="PUT YOUR MAP API KEY" />
<LinearLayout android:layout_width="fill_parent"
android:layout_alignParentBottom="true" android:layout_height="wrap_content"
android:orientation="vertical">
<EditText android:layout_width="fill_parent" android:id="@+id/location"
android:layout_height="wrap_content" android:text="tian an meng" />
<Button android:id="@+id/geocodeBtn" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Find Location" />
</LinearLayout>
</RelativeLayout>
Java代碼:
public class GeocodingDemoActivity extends MapActivity {
public static final String TAG = "GeocodingDemoActivity";
Geocoder geocoder = null;
MapController mMapController;
public MapView mapView;
ProgressDialog progDialog = null;
List<Address> addressList = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geocode);
int lat = (int) (31.83659536 * 1000000);
int lng = (int) (117.1912658 * 1000000);
mapView = (MapView) findViewById(R.id.geoMap);
mMapController = mapView.getController();
GeoPoint curpt = new GeoPoint(lat, lng);
mMapController.animateTo(curpt);
mapView.setBuiltInZoomControls(true);
mMapController.setZoom(15);
geocoder = new Geocoder(this);
Button geoBtn = (Button) findViewById(R.id.geocodeBtn);
geoBtn.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
EditText loc = (EditText) findViewById(R.id.location);
String locationName = loc.getText().toString();
progDialog = ProgressDialog.show(GeocodingDemoActivity.this,
"Processing...", "Finding Location...", true, false);
findLocation(locationName);
}
});
}
protected boolean isLocationDisplayed() {
return false;
}
protected boolean isRouteDisplayed() {
return false;
}
private void findLocation(final String locationName) {
//使用一個新的線程來實現地理位置的編碼
Thread thrd = new Thread() {
public void run() {
try {
// do backgrond work
addressList = geocoder.getFromLocationName(locationName, 5);
// send message to handler to process results
uiCallback.sendEmptyMessage(0);
} catch (IOException e) {
e.printStackTrace();
}
}
};
thrd.start();
}
// ui thread callback handler
private Handler uiCallback = new Handler() {
@Override
public void handleMessage(Message msg) {
progDialog.dismiss();
//打印出本線程的ID
Log.i(TAG, "Handler Thread :" + Thread.currentThread().getId());
if (addressList != null && addressList.size() > 0) {
int lat = (int) (addressList.get(0).getLatitude() * 1000000);
int lng = (int) (addressList.get(0).getLongitude() * 1000000);
GeoPoint pt = new GeoPoint(lat, lng);
mapView.getController().setZoom(15);
mapView.getController().setCenter(pt);
} else {
Dialog foundNothingDlg = new AlertDialog.Builder(
GeocodingDemoActivity.this).setIcon(0)
.setTitle("Failed to Find Location")
.setPositiveButton("Ok", null)
.setMessage("Location Not Found...").create();
foundNothingDlg.show();
}
}
};
}
下面詳解 AsyncTask的使用,學習一個方法前,應該很仔細的看官方的文檔,這樣我們會對它的機制更熟悉一些,再參考一些具體的實例,加深理解,
下面的一個實例使用AsyncTask加載一個網頁的內容,並可有進度條顯示加載的進度。實例來源於網絡,作者還沒找到:
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">
<Button android:id="@+id/execute" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="execute" />
<Button android:id="@+id/cancel" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:enabled="false"
android:text="cancel" />
<ProgressBar android:id="@+id/progress_bar"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:progress="0" android:max="100"
style="?android:attr/progressBarStyleHorizontal" />
<ScrollView android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/text_view" android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
Java代碼:
public class AsyncTaskTset extends Activity {
private static final String TAG = "ASYNC_TASK";
private static final String URL = "http://www.google.com.hk/";
private Button execute;
private Button cancel;
private ProgressBar progressBar;
private TextView textView;
private MyTask mTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.asynctask);
execute = (Button) findViewById(R.id.execute);
execute.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//注意每次需new一個實例,新建的任務只能執行一次,否則會出現異常
mTask = new MyTask();
mTask.execute(URL);
execute.setEnabled(false);
cancel.setEnabled(true);
}
});
cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//取消一個正在執行的任務,onCancelled方法將會被調用
mTask.cancel(true);
}
});
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
textView = (TextView) findViewById(R.id.text_view);
}
//三個參數
/**
* 1、Params 啓動任務執行的輸入參數,比如HTTP請求的URL
* 2、Progress 後臺任務執行的百分比
* 3、Result 後臺執行任務最終返回的結果,比如String,也可以是一個image
*/
private class MyTask extends AsyncTask<String, Integer, String> {
//onPreExecute方法用於在執行後臺任務前做一些UI操作
protected void onPreExecute() {
Log.i(TAG, "onPreExecute() called");
textView.setText("loading...");
}
//doInBackground方法內部執行後臺任務,不可在此方法內修改UI
protected String doInBackground(String... params) {
Log.i(TAG, "doInBackground(Params... params) called");
try {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(params[0]);
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
long total = entity.getContentLength();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int count = 0;
int length = -1;
while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
count += length;
//調用publishProgress公佈進度,最後onProgressUpdate方法將被執行
publishProgress((int) ((count / (float) total) * 100));
//爲了演示進度,休眠500毫秒
Thread.sleep(500);
}
return new String(baos.toByteArray(), "gb2312");
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return null;
}
//onProgressUpdate方法用於更新進度信息
protected void onProgressUpdate(Integer... progresses) {
Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
progressBar.setProgress(progresses[0]);
textView.setText("loading..." + progresses[0] + "%");
}
//onPostExecute方法用於在執行完後臺任務後更新UI,顯示結果
protected void onPostExecute(String result) {
Log.i(TAG, "onPostExecute(Result result) called");
textView.setText(result);
execute.setEnabled(true);
cancel.setEnabled(false);
}
//onCancelled方法用於在取消執行中的任務時更改UI
protected void onCancelled() {
Log.i(TAG, "onCancelled() called");
textView.setText("cancelled");
progressBar.setProgress(0);
execute.setEnabled(true);
cancel.setEnabled(false);
}
}
}
一、補充知識點:
摘錄於:http://www.android123.com.cn/androidkaifa/422.html
1. 對於線程中的刷新一個View爲基類的界面,可以使用postInvalidate()方法在線程中來處理,其中還提供了一些重寫方法比如postInvalidate(intleft,int top,int right,int bottom) 來刷新一個矩形區域,以及延時執行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,intleft,int top,int right,int
bottom) 方法,其中第一個參數爲毫秒
2. 當然推薦的方法是通過一個Handler來處理這些,可以在一個線程的run方法中調用handler對象的 postMessage或sendMessage方法來實現,Android程序內部維護着一個消息隊列,會輪訓處理這些,如果你是Win32程序員可以很好理解這些消息處理,不過相對於Android來說沒有提供 PreTranslateMessage這些干涉內部的方法。
3. Looper又是什麼呢? ,其實Android中每一個Thread都跟着一個Looper,Looper可以幫助Thread維護一個消息隊列,但是Looper和Handler沒有什麼關係,我們從開源的代碼可以看到Android還提供了一個Thread繼承類HanderThread可以幫助我們處理,在HandlerThread對象中可以通過getLooper方法獲取一個Looper對象控制句柄,我們可以將其這個Looper對象映射到一個Handler中去來實現一個線程同步機制,Looper對象的執行需要初始化Looper.prepare方法就是昨天我們看到的問題,同時推出時還要釋放資源,使用Looper.release方法。
4.Message 在Android是什麼呢? 對於Android中Handler可以傳遞一些內容,通過Bundle對象可以封裝String、Integer以及Blob二進制對象,我們通過在線程中使用Handler對象的sendEmptyMessage或sendMessage方法來傳遞一個Bundle對象到Handler處理器。對於Handler類提供了重寫方法handleMessage(Message msg) 來判斷,通過msg.what來區分每條信息。將Bundle解包來實現Handler類更新UI線程中的內容實現控件的刷新操作。相關的Handler對象有關消息發送sendXXXX相關方法如下,同時還有postXXXX相關方法,這些和Win32中的道理基本一致,一個爲發送後直接返回,一個爲處理後才返回
.
5. java.util.concurrent對象分析,對於過去從事Java開發的程序員不會對Concurrent對象感到陌生吧,他是JDK 1.5以後新增的重要特性作爲掌上設備,我們不提倡使用該類,考慮到Android爲我們已經設計好的Task機制,這裏不做過多的贅述,相關原因參考下面的介紹:
6. 在Android中還提供了一種有別於線程的處理方式,就是Task以及AsyncTask,從開源代碼中可以看到是針對Concurrent的封裝,開發人員可以方便的處理這些異步任務。
二、可能涉及到得面試題:handler機制的原理
andriod提供了 Handler 和 Looper 來滿足線程間的通信。Handler 先進先出原則。Looper類用來管理特定線程內對象之間的消息交換(Message Exchange)。
1)Looper: 一個線程可以產生一個Looper對象,由它來管理此線程裏的Message Queue(消息隊列)。
2)Handler: 你可以構造Handler對象來與Looper溝通,以便push新消息到Message Queue裏;或者接收Looper從Message Queue取出)所送來的消息。
3) Message Queue(消息隊列):用來存放線程放入的消息。
4)線程:UI thread 通常就是main thread,而Android啓動程序時會替它建立一個Message Queue。
如要要更加深入的瞭解,可以參考Sundy的視頻講解。