轉載網址: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); } }; }