Android在android.os包中定義了兩個類,它們通常是多線程應用線程間通信的基石:Handler和Looper
AsyncTask對象隱藏了Handler和Looper的細節,在某些情況還是要直接跟Handler和Looper打交道,比如把Runnnable對象傳遞到主線程之外的線程
Message:消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。
Handler:處理者,負責Message的發送及處理。使用Handler時,需要實現handleMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等。
MessageQueue:消息隊列,用來存放Handler發送過來的消息,並按照FIFO規則執行。當然,存放Message並非實際意義的保存,而是將Message以鏈表的方式串聯起來的,等待Looper的抽取。
Looper:消息泵,不斷地從MessageQueue中抽取Message執行。因此,一個MessageQueue需要一個Looper。
Thread:線程,負責調度整個消息循環,即消息循環的執行場所。
Handler的使用:
public class MyThread extends Thread{
private static final String TAG="MyThread";
private Handler mHandler;
public MyThread(String name) {
super(name);
}
public Handler getmHandler() {
return mHandler;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
Looper.prepare();//把Looper綁定到此線程
mHandler=new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
//處理消息
}
}
};
//Handler綁定到此線程的Looper
Looper.loop();//調用loop()去啓動消息循環
}
}
注:Handler對象在run()方法中創建,因爲它需要綁定到指定的Looper,這個Looper就是在run()方法中調用Looper.prepare()創建的,因此在線程產生之前,調用getHandler() 將返回null
Looper的使用:
public class MyHandlerThread extends HandlerThread{
private static final String TAG="MyHandlerThread";
private Handler mHandler;
public MyHandlerThread(String name) {
super(name);
}
public Handler getmHandler() {
return mHandler;
}
public void start() {
// TODO Auto-generated method stub
super.start();
Looper looper=getLooper();//這裏會一直阻塞到線程的Looper對象初始化結束
mHandler=new Handler(looper){
public void handleMessage(Message msg){
switch(msg.what){
//處理消息
}
}
};
}
}
android中的線程
1. 不要阻塞UI線程. 如果在UI線程中執行阻塞或者耗時操作會導致UI線程無法響應用戶請求.
2. 不能在非UI線程(也稱爲工作線程)中更新UI, 這是因爲android的UI控件都是線程不安全的.
由上所述, 開發者經常會啓動工作線程完成耗時操作或阻塞操作, 如果需要在工作線程的執行期間更新UI狀態, 則應該通知UI線程來進行.
線程間通信
請看下面的代碼:
- public void onClick(View v) {
- new Thread(new Runnable() {
- public void run() {
- Bitmap b = loadImageFromNetwork("http://example.com/image.png");
- mImageView.setImageBitmap(b);
- }
- }).start();
- }
上面的代碼是錯誤的, mImageView.setImageBitmap(b)違反了第二條準則--不能在工作線程中更新UI.
線程間通信可以解決工作線程如何通知UI線程更新控件的問題. android提供了3種線程間通信的方案:
1. 調用以下方法:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
如果在工作線程中調用了這3個方法, 那麼方法中Runnable參數封裝的操作會在UI線程中執行.
使用這種方式可以修正例子中的錯誤之處:
- public void onClick(View v) {
- new Thread(new Runnable() {
- public void run() {
- final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
- mImageView.post(new Runnable() {
- // run方法會在UI線程中執行
- public void run() {
- mImageView.setImageBitmap(bitmap);
- }
- });
- }
- }).start();
- }
2. Handler機制. Handler機制允許開發者在工作線程中調用與UI線程綁定的handler對象的sendMessage()方法向UI線程的消息隊列發送一條消息, UI線程會在適當的時候從消息隊列中取出消息並完成處理.
3. 使用AsyncTask類. 創建一個AsyncTask類的子類, 並根據需要選擇覆寫onPreExecute(), doInBackground(), onProgressUpdate(), onPostExecute()方法.AsyncTask類的具體使用方法請參看文檔, 以下是一些大概的說明:
a. AsyncTask類是一個泛型類, 存在3個泛型參數. 第一個參數指定execute方法的參數類型, 第二個參數指定onProgressUpdate()方法的參數類型, 第三個參數指定 doInBackground()方法的返回值類型以及onPostExecute()方法的參數類型.
b. 執行流程: 在UI線程中調用AsyncTask類的execute方法(只有該步驟是由程序員控制的)-->系統調用onPreExecute(), 這個方法在UI線程中執行-->系統調用doInBackground()方法, 這個方法在工作線程中執行-->在doInBackground()方法中每調用一次publishProgress()方法, 就會在UI線程中執行一次onProgressUpdate()方法-->doInBackground()方法執行完成後, 系統將調用 onPostExecute()方法, 並將doInBackground()方法的返回值傳遞給 onPostExecute()方法的形參. onPostExecute()方法在UI線程中執行.
採用這種方式也可以修正例子中的錯誤之處:
- public void onClick(View v) {
- new DownloadImageTask().execute("http://example.com/image.png");
- }
- private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
- protected Bitmap doInBackground(String... urls) {
- return loadImageFromNetwork(urls[0]);
- }
- protected void onPostExecute(Bitmap result) {
- mImageView.setImageBitmap(result);
- }
-
}