為什么要將thread對象post到handler中執行呢?


轉載網址:http://www.cnblogs.com/crazypebble/archive/2011/03/23/1991829.html
在Android中使用Handler和Thread線程執行后台操作

對於線程的控制,我們將介紹一個 Handler類,使用該類可以對運行在不同線程中的多個任務進行排隊,並使用Message和Runnable對象安排這些任務。在javadoc中,對Handler是這樣解釋的:Handler可以發送和處理消息對象或Runnable對象,這些消息對象和Runnable對象與一個線程相關聯。每個Handler的實例都關聯了一個線程和線程的消息隊列。當創建了一個Handler對象時,一個線程或消息隊列同時也被創建,該Handler對象將發送和處理這些消息或Runnable對象。

  下面有幾種對Handler對象的構造方法需要了解一下:

a、如果new一個無參構造函數的Handler對象,那么這個Handler將自動與當前運行線程相關聯,也就是說這個Handler將與當前運行的線程使用同一個消息隊列,並且可以處理該隊列中的消息。

private Handler handler = new Handler();

  我們做這樣一個實驗,在主用戶界面中創建一個帶有無參構造函數的Handler對象,該Handler對象向消息隊列推送一個Runnable對象,在Runnable對象的run函數中打印當前線程Id,我們比較主用戶界面線程ID和Runnable線程ID是否相同。具體代碼如下:

public class HandlerTest01 extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

System.out.println("Activity ---> " + Thread.currentThread().getId());
handler.post(r);
}

private Handler handler = new Handler();
private Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Runnalbe ---> " + Thread.currentThread().getId());
}
};
}

  通過這個例子的輸出可以發現,Runnable對象和主用戶界面線程的ID是相同。在這個例子中,我們直接利用handler對象post了一個runnable對象,相當於直接調用了Runnable對象的run函數,也就說沒有經過start函數調用run(),那么就不會創建一個新線程,而是在原有線程內部直接調用 run()方法,因此輸出的線程Id是相同的。

b、如果new一個帶參構造函數的Handler對象,那么這個Handler對象將與參數所表示的Looper相關聯。注意:此時線程類應該是一個特殊類HandlerThread類,一個Looper類的Thread類,它繼承自Thread類。

HandlerThread handlerthread = new HandlerThread("MyThread");
handlerthread.start();
private MyHandler handler = new MyHandler(handlerthread.getLooper());

class MyHandler extends Handler {
public MyHandler() {

}

public MyHandler(Looper looper) {
super(looper);
}
}

  下面這個例子,將介紹如何開啟一個新的線程,並通過Handler處理消息。

public class HandlerTest02 extends Activity {

private MyHandler myhandler = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.setContentView(R.layout.main);
System.out.println("Activity ---> " + Thread.currentThread().getId());

// 生成一個HandlerThread對象,使用Looper來處理消息隊列
HandlerThread thread = new HandlerThread("MyThread");
// 必須啟動這個線程
thread.start();
// 將一個線程綁定到Handler對象上,則該Handler對象就可以處理線程的消息隊列
myhandler = new MyHandler(thread.getLooper());
// 從Handler中獲取消息對象
Message msg = myhandler.obtainMessage();
// 將msg對象發送給目標對象Handler
msg.sendToTarget();
}

class MyHandler extends Handler {
public MyHandler() {

}

// 帶有參數的構造函數
public MyHandler(Looper looper) {
super(looper);
}

@Override
public void handleMessage(Message msg) {
System.out.println("MyHandler ---> " + Thread.currentThread().getId());
}
}
}

  根據這個例子返回的結果,可以看出,新線程Id與主用戶界面的線程Id不同。由於我們調用了thread.start()方法,真正的創建了一個新線程,與原來的線程處於不同的線程上下文中,因此打印輸出的線程Id是不同的。
c、如果需要Handler對象去處理消息,那么就要重載Handler類的handleMessage函數。

private Handler handler = new Handler() {

@Override
public void handleMessage(Message msg) {
// TODO : Handle the msg
// Usually we update UI here.
}
}

  注意到注釋部分,我們通常在handleMessage中處理更新UI界面的操作。

  前面介紹了Handler類的基本使用,但是還是沒有涉及到Thread類。要想實現在后台重新開啟一個新的線程,通過該線程執行一些費時的操作,我們也使用Thread類來完成這個功能。下面我們先給出一個使用Thread類的例子程序。

public class ThreadTest extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.setContentView(R.layout.main);
System.out.println("Activity ---> " + Thread.currentThread().getId());

Thread thread = new Thread(r);
thread.start();
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
thread.stop();
}

Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Runnable ---> " + Thread.currentThread().getId());
}
};
}

  這個程序執行的結果如下。新線程在創建對象時,傳入了Runnable類的一個對象,在Runnable對象中重載了run()方法去執行耗時的操作;新的線程實例執行了start方法,開啟了一個新的線程執行Runnable的run方法。

上面這些就是我現在接觸到執行線程的方法,在線程中,可以完成我們所需要的操作(比如:下載,處理數據,檢測網絡狀態等),使其與UI界面分離,那么UI界面不會因為耗時操作導致界面被阻塞。

  在《解密Google Android》一書中,發現了這樣一個啟動線程的模型。利用該模型,我們可以把一些耗時的操作放到doStuff方法中去執行,同時在 updateUIHere方法中進行更新UI界面的操作,就可以完成一個線程所需要的功能。其他的說明寫在注釋部分了。

Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
updateUIHere();
}
}

new Thread() {
public void run() {
doStuff(); // 執行耗時操作
Message msg = myHandler.obtainMessage();
Bundle b = new Bundle();
b.putString("key", "value");
m.setData(b); // 向消息中添加數據
myHandler.sendMessage(m); // 向Handler發送消息,更新UI
}
}.start();

======================================================================================================
通過上面的文章,可以對handler的處理機制有一個比較深刻的認識,而我之前的疑問,為什么在handler中post一個線程,也就有了答案。
handler的應用相當一個對多線程管理的機制,通過一個線程的隊列(因為可以post多個)來管理這些線程,
同時通過使用Handler或者HandlerThread來控制,這些線程是要在主線程中執行還是要新開一個線程來執行。

另外一個不錯的例子
參考網址:http://rayln.iteye.com/blog/1180652

public class HandlerActivity extends Activity {  
         /** Called when the activity is first created. */  
         @Override  
         public void onCreate(Bundle savedInstanceState) {  
           super.onCreate(savedInstanceState);  
           setContentView(R.layout.main);  
           Button b1 = (Button)findViewById(R.id.button1);  
           Button b2 = (Button)findViewById(R.id.button2);  
           b1.setOnClickListener(new OnClickListener() {  
               @Override  
               public void onClick(View v) {  
                   //立即把線程加入消息隊列中  
                   handler.post(r);  
               }  
           });  
           b2.setOnClickListener(new OnClickListener() {  
               @Override  
               public void onClick(View v) {  
                   //停止線程  
                   handler.removeCallbacks(r);  
               }  
           });  
         }  
         Handler handler = new Handler();  
   /**
        *該方法的內部類將在handler.sendMessage(msg)后執行
            Handler handler = new Handler(){
           @Override
           public void handleMessage(Message msg) {
               System.out.println("msg:"+msg.arg1);
           }
           };
       */  
         Runnable r = new Runnable() {  
           @Override  
           public void run() {  
               System.out.println("sysout Thread");  
               //得到一個消息對象,Message類是有Android系統  
               Message msg = handler.obtainMessage();  
               //將msg對象arg1參數設置為122,用arg1和arg2傳遞消息  
               //優點是系統資源消耗較小  
               msg.arg1 = 122;  
               //將消息加入到另外一個消息隊列中去  
               handler.sendMessage(msg);  
               //3000毫秒后加入線程到消息隊列中  
               handler.postDelayed(r, 3000);  
           }  
         };  
   }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM