2011年12月26日 星期一

Android - Pass Data (5) - 不同Thread之間傳遞資料

很多情況下,我們會提供一個UI (Activity)給User操作,但是一些比較耗時的計算可能用另一個Thread執行。重點來了,如果只是在Activity中使用 Handler,然後post一個有 implements Runnable的物件去執行耗時的工作,那是行不通的。程式如下:

public class MainThread extends Activity {
private Handler handler = new Handler();
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler.post(new MyRunnable());  
...
...
...
  }
  private class MyRunnable implements Runnable {
      //耗時的工作

  }
}

這樣的程式寫法,依然有可能讓UI有停頓的感覺,甚至是ANR的錯誤發生。因為上述寫法的MyRunnable其實和MainThread都是在同一條thread裡面工作,並沒有真正拉出一條新的thread。為了解決這個問題可以利用Handler、HandlerThread來達成。


下面程式順便呈現如何把資料由原本的main thread 丟到 thread。

程式碼如下:
public class MainThread extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        Log.d("TestHandlerThread","The MainThread is :"+Thread.currentThread().getId());
       
        //"HandlerThread"隨便你取,後面參數決定Thread的優先權高低,
        //因為要背景作業,所以不用選太高
        Log.d("TestHandlerThread","new HandlerThread()");
        HandlerThread myHandlerThread = new HandlerThread("HandlerThread",Process.THREAD_PRIORITY_BACKGROUND);
       
        Log.d("TestHandlerThread","before start()");
        myHandlerThread.start();
       
        Log.d("TestHandlerThread","before getLooper()");
        MyHandler myHandler = new MyHandler(myHandlerThread.getLooper());
       
        Log.d("TestHandlerThread","obtainMessage()");
        Message myMessage = myHandler.obtainMessage();
       
        //Bundle的物件就是用來傳遞用的
        Bundle myBundle = new Bundle();
        myBundle.putString("name", "Gill");
        myMessage.setData(myBundle);
       
        Log.d("TestHandlerThread","before sendToTarget()");
        myMessage.sendToTarget();   
    }
   
    class MyHandler extends Handler{
        public MyHandler(){
        }
        public MyHandler(Looper looper){
            super(looper);
            Log.d("TestHandlerThread","MyHandler/MyHandler(Looper looper)");           
        }
        @Override
        public void handleMessage(Message msg) {
            //這裡可以在接收到所需的資料後,再進行一些比較耗時的工作
            Log.d("TestHandlerThread","MyHandler/handleMessage");
            Log.d("TestHandlerThread","The MyHandler is :"+Thread.currentThread().getId());
           
            Bundle myBundle = msg.getData();
            String myName = myBundle.getString("name");
            Toast.makeText(MainThread.this, myName, Toast.LENGTH_LONG).show();
        }
    }
}

執行結果如下:


















附上程式執行的Log檔:
D/TestHandlerThread(  310): The MainThread is :1
D/TestHandlerThread(  310): new HandlerThread()
D/TestHandlerThread(  310): before start()
D/TestHandlerThread(  310): before getLooper()
D/TestHandlerThread(  310): MyHandler/MyHandler(Looper looper)
D/TestHandlerThread(  310): obtainMessage()
D/TestHandlerThread(  310): before sendToTarget()
D/TestHandlerThread(  310): MyHandler/handleMessage
D/TestHandlerThread(  310): The MyHandler is :8

從Log檔可以發現,兩條Thread確實是分開的。另外在呼叫了sendToTarget()之後,就會去handleMessage()進行處理。

沒有留言:

張貼留言