android經過兩種方法開啓一個線程java
你們都知道,在PC上的應用程序當須要進行一些複雜的數據操做,但不須要界面UI的時候,咱們會爲應用程序專門寫一個線程去執行這些複雜的數據操做。經過線程,能夠執行例如:數據處理、數據下載等比較耗時的操做,同時對用戶的界面不會產生影響。在Android應用程序開發中,一樣會遇到這樣的問題。當咱們須要訪問網絡,從網上下載數據並顯示在咱們的UI上時,就會啓動後臺線程去下載數據,下載線程執行完成後將結果返回給主用戶界面線程。android
對於線程的控制,咱們將介紹一個Handler類,使用該類能夠對運行在不一樣線程中的多個任務進行排隊,並使用Message和Runnable對象安排這些任務。在javadoc中,對Handler是這樣解釋的:Handler能夠發送和處理消息對象或Runnable對象,這些消息對象和Runnable對象與一個線程相關聯。每一個Handler的實例都關聯了一個線程和線程的消息隊列。當建立了一個Handler對象時,一個線程或消息隊列同時也被建立,該Handler對象將發送和處理這些消息或Runnable對象。網絡
下面有幾種對Handler對象的構造方法須要瞭解一下:ide
a、若是new一個無參構造函數的Handler對象,那麼這個Handler將自動與當前運行線程相關聯,也就是說這個Handler將與當前運行的線程使用同一個消息隊列,而且能夠處理該隊列中的消息。函數
private Handler handler = new Handler();oop
咱們作這樣一個實驗,在主用戶界面中建立一個帶有無參構造函數的Handler對象,該Handler對象向消息隊列推送一個Runnable對象,在Runnable對象的run函數中打印當前線程Id,咱們比較主用戶界面線程ID和Runnable線程ID是否相同。具體代碼以下:
post
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是相同的。this
b、若是new一個帶參構造函數的Handler對象,那麼這個Handler對象將與參數所表示的Looper相關聯。注意:此時線程類應該是一個特殊類HandlerThread類,一個Looper類的Thread類,它繼承自Thread類。spa
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處理消息。線程
HandlerTest02.java
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類的例子程序。
ThreadTest.java
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();