Thread && Handler
Thread
實作Thread常用的使用方法有2種。一種是繼承Thread class,再override實現run() method;另一種是implements Runnable(實作Runnable interface), 再override實現run() method。
情境一:使用extends Thread
public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
System.out.println("Thread run() method executed.");
}
}
//Started with a "new MyThread().start()" call
或者這樣簡寫:
Thread t = new Thread() {
@Override
public void run() {
// Block of code to execute
// worker thread(background thread),處理需長時間或大量的運算
System.out.println("Thread run() method executed.");
}
};
t.start();
情境二:使用implements Runnable Interface
public class MyRunnable implements Runnable {
public void run() {
//Code
System.out.println("Runnable run() method executed.");
}
}
//Started with a "new Thread(new MyRunnable()).start()" call
或者這樣簡寫:
Runnable r = new Runnable() {
@Override
public void run() {
// Block of code to execute
System.out.println("Runnable run() method executed.");
}
};
Thread t = new Thread(r);
t.start();
關於這兩種情境的差異在哪裡,是很值得去探討的。相關資料可以參考: “implements Runnable” vs. “extends Thread”
執行在UI Thread
如果我們想 Runnable ,是執行在UI thread,可以呼叫 Activity.runOnUiThread()
method。假設目前的thread已經是在UI thread中,Runnable會立即的去執行。否則會透過UI thread的 MessageQueue 排隊,此方法如下:
new Handler(Looper.getMainLooper()).post(runnable);
Note: The View class has a post()) method too, which lets you add a Runnable to the UI thread’s MessageQueue. Hence, the runnable will be run on the user interface thread. You can read this SO thread to understand the difference between
Activity.runOnUiThread()
andView.post()
.
以上面例子可以改寫成
Runnable r = new Runnable() {
@Override public void run() {
// Block of code to execute
System.out.println("Runnable run() method executed.")
((Activity) mContext).runOnUiThread(new Runnable() {
public void run() {
//這邊是呼叫main thread handler幫我們處理UI部分
System.out.println("call UI thread to deal with UI part")
}
});
}
};
Thread t = new Thread(r); t.start();
或者
view.post(new Runnable(){
public void run(){
//update UI
}
});
Handler
Android中的handler ( android.os.Handler → java.lang.Object ) 基本上和Java中的Handler (java.util.logging.Handler → java.lang.Object)從架構上看來沒有關係。
Android的handler是作為傳遞訊息(android.os.Message)和啟動Runnable對任務。
- thread處理訊息機制包含 Handler、Looper、Message and MessageQueue 。
android.os.Looper
- 一個Thread只能有一個Looper。
- 當Message處理完畢後, 會將Message發送給Handler。
android.os.Handler
- 一個Thread可以有多個Handler。
- 負責將Message送往MessageQueue, 並且接收Looper丟出來的Message。
android.os.MessageQueue
- 一個Thread只能有一個MessageQueue。
- 負責裝載Message的佇列(queue),對Message進行管理, 是一個無限制的鏈結串列(linklist)。
android.os.Message
- thread上要處理的訊息。
- When creating a Handler object you’ve to pass the Looper object as the first argument to the constructor explicitly. If not then it binds to the Looper of the current thread. If you try to be implicit and the thread doesn’t have a Looper then a
RuntimeException
will be thrown.Note: Multiple Handlers doesn’t ensure concurrent execution as the Messages\/Runnables will be posted in the queue from which they’re processed sequentially.
Handler mHandler;
class MyThread implements Runnable {
@Override
public void run() {
Message msg = Message.obtain();
msg.obj = "My message!";
mHandler.sendMessage(msg);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
System.out.println(msg.obj);
}
};
Thread t = new Thread(new MyThread());
t.start();
}
Handler負責派送訊息, 交給MessageQueue進行排隊, 再透過Looper將每一個Message Object丟給Handler處理。 以下是透過MainThread上面的Looper進行處理。
可以透過 Main Thread的Handler來處理比較少量資料。
new Handler(mContext.getMainLooper()).post(new Runnable(){
public void run() {
//處理少量資訊或UI
}
});
可以使用delay的方法,延後thread的處理。
new Handler().postDelayed(new Runnable(){
public void run(){
//處理少量資訊或UI
}
}, 3000); //延後3秒提交任務
當然也可以自己定義自己的Looper。(範例來源)
new Thread(new Runnable() {
public void run() {
Log.e(TAG, "!!<A");
Looper.prepare();
new Handler().post(new Runnable(){
@Override public void run() {
Log.e(TAG, "B1");
}
});
new Handler().post(new Runnable(){
@Override public void run() {
Log.e(TAG, "B2");
Looper.myLooper().quit();
}
});
Looper.loop();
Log.e(TAG, "C");
((Activity) mContext).runOnUiThread(new Runnable() {
public void run() {
Log.e(TAG, "D");
}
});
}
}).start();
輸出為A、B1、B2、C、D由上面的程式碼可以看到我們自己定義的Looper,對於Thread內的Handler會變成跟自己定義的Looper進行綁定,也就是說這邊是屬於Background Thread部分, 不行拿來更新UI,而直到呼叫Looper內的quit, 則會將該Looper以及MessageQueue移除, Thread才會繼續往下執行,注意: 假設這邊如果還有Message正在處理, 則會將該Message處理完畢, 再將後面未處理的Message全部移除。