如何使用Thread和Handler之二(Handler的post&sendMessage差異)

如何使用Thread和Handler之二(Handler的post&sendMessage差異)

先前有寫過簡單的範例, 來示範怎麼使用Thread&Handler
請參考如何使用Thread和Handler

這篇想討論Handler的兩個方法, post跟sendMessage的差異。

如何使用Thread和Handler這篇知道sendMessage的使用方法,
會先開啟一隻Thread去執行我們複雜的計算,
之後透過Handler的物件去發送一個Message物件,
用來通知UI Thread更新畫面。

而Handler的post方法, 我想知道跟sendMessage差別在哪裡,
因此作了以下的實驗。

private Handler mHandler = new Handler(){
    public void handleMessage(Message msg){
        switch(msg.what){
           case 0:
               Log.e("HandlerThreadID", Long.toString(Thread.currentThread().getId()));
               Log.e("HandlerThreadName", Thread.currentThread().getName());
           break;
        }
    }
};

private Thread mThread = new Thread(new Runnable() {
    public void run() {
        Log.e("ThreadID", Long.toString(Thread.currentThread().getId()));
        Log.e("ThreadName", Thread.currentThread().getName());
        Message msg = new Message();
        msg.what = 0;
        handler.sendMessage(msg);

    }
});

@Override
public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    mThread.start();
    Log.e("ActivityThreadID",
    Long.toString(Thread.currentThread().getId()));
    Log.e("ActivityThreadName",Thread.currentThread().getName());
}

印出來的結果是這樣

ThreadID: 78
ThreadName: Thread-78
ActivityThreadID: 1
ActivityThreadName: main
HandlerThreadID: 1
HandlerThreadName: main

由上面結果可以知道, 這樣的做法會產生一支新的Thread來幫我們執行程式。

接著是另外一種Handler.post的方法, 這種作法野蠻常見的,
將實作好的Runnable物件丟進post讓Handler去執行。

private class MyHandler extends Handler{
    public MyHandler(){
       Log.e("HandlerThreadID", Long.toString(Thread.currentThread().getId()));
       Log.e("HandlerThreadName",Thread.currentThread().getName());

    }
}

@Override
public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    MyHandler mHandler2 = new MyHandler();
    mHandler2.post(new Runnable(){
       @Override
       public void run() {
          // TODO Auto-generated method stub
          Log.e("RunnableThreadID", Long.toString(Thread.currentThread().getId()));
          Log.e("RunnableThreadName",Thread.currentThread().getName());

       }
   });

   //mThread.start();
   Log.e("ActivityThreadID", Long.toString(Thread.currentThread().getId()));
   Log.e("ActivityThreadName",Thread.currentThread().getName());
}

印出來的結果是

HandlerThreadID: 1
HandlerThreadName: main
ActivityThreadID: 1
ActivityThreadName: main
RunnableThreadID: 1
RunnableThreadName: main

從上面結果來看, 並沒有產生新的Thread來幫我們執行程式,
因此產生幾種猜測

1. sendMessage在之前, 我們有開一支新的Thread並且start(), 因此會產生新的Thread
2. post並不會產生新的Thread, sendMessage會
3. 因為post傳入的是Runnable, 因此不會產生新的Thread

先來驗證第三種說法

private class MyThread extends Thread{
    public MyThread(Runnable r){
        super(r);
        Log.e("ThreadID",Long.toString(Thread.currentThread().getId()));
        Log.e("ThreadName",Thread.currentThread().getName());
    }
}

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MyHandler mHandler2 = new MyHandler();
    mHandler2.post(new MyThread(new Runnable(){
       @Override
       public void run() {
          // TODO Auto-generated method stub
          Log.e("RunnableThreadID", Long.toString(Thread.currentThread().getId()));
          Log.e("RunnableThreadName",Thread.currentThread().getName());

       }
   }));
   //mThread.start();

   Log.e("ActivityThreadID", Long.toString(Thread.currentThread().getId()));
   Log.e("ActivityThreadName",Thread.currentThread().getName());

}

結果是

HandlerThreadID: 1
HandlerThreadName: main
ThreadID: 1
ThreadName: main
ActivityThreadID: 1
ActivityThreadName: main
RunnableThreadID: 1
RunnableThreadName: main

由結論得知, 即使傳入一支Thread, Handler也不會幫我們啟動它,
因此第三種猜測不攻自破。

接下來驗證第二種猜測, 怎麼驗證呢?

最簡單的就是爬Handler source code,
找到以下的網站可以看見Android的source code,
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.1_r2/android/os/Handler.java#Handler.Callback

爬的過程就不細說了, 有興趣的人可以去爬看看XD
流程是我們傳入一個Runnable的物件進去, 最後會被丟進Message物件的Runnable變數,
而這個Message會被送往MessageQueue裡面等待被處理,
當輪到這個Message物件被處理的時候, 就會被Looper這個東西來啟動,
最後我們傳進去Runnable物件就會被run起來。

因此從這邊可以得到一個資訊, 並沒有產生任何新的Thread,
而順便去看一下sendMessage的source code, 發現流程也是相同的,
所以第二種猜測也是不正確的。

我在網路上看到很多說法是第二種,
post並不會產生新的Thread, sendMessage會
這是錯誤的!!!!

所以結論是
除非我們自己寫一個Thread並且start它,
否則sendMessage或者post都不會產生新的Thread。