[android] Android 一樣也有 GUI thread 的問題

  • 21173
  • 0

[android] Android 一樣也有 GUI thread 的問題

我自從 VB 轉到 VB.NET, C# 之後,每次寫與 GUI 相關的東西,總是要知道一件事:在 multi-thread 的情況下,另一個 thread 如何更新 GUI 上的資料。在以前 VB 的時代,總沒有這種需求,但是到了 .NET 之後,一定都要考量這件事。

於是,到了 android,是不是也一樣呢?於是找了網路上的 thread 的範例改寫成每隔一秒更新 textview 的文字的程式來測試:

package com.example.uithread;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

    mythread mt;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mt=new mythread();
        mt.start();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    class mythread extends Thread{

        /* (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        @Override
        public void run() {
            TextView tv = (TextView)findViewById(R.id.textview1);
            for (int i=0;i<10;i++){
                try{
                    Thread.sleep(1000);                   
                }catch(InterruptedException ex){
                    ex.printStackTrace();
                }
                tv.setText("mythread run:" + String.valueOf(i));
            }
        }
    }
}

一執行就得到 crash,錯誤是這樣子的:

02-18 06:05:55.336: E/AndroidRuntime(555): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
......................................................
02-18 06:05:55.336: E/AndroidRuntime(555):     at com.example.uithread.MainActivity$mythread.run(MainActivity.java:43)

第 43 行就是

tv.setText("mythread run:" + String.valueOf(i));

證明「非 GUI thread」沒辦法更新 GUI 上的顯示資料。在 .NET 的世界有標準做法,那 android 呢?據說是用 Handler 這個物件的 sendMessage 與 handleMessage 來處理。我改了個小範例,看起來就正常的樣子:

package com.example.uithread;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

    MyThread mt;
    MyHandler mh;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /* (non-Javadoc)
     * @see android.app.Activity#onResume()
     */
    @Override
    protected void onResume() {
        super.onResume();
        mh=new MyHandler();
        mt=new MyThread();
        mt.start();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    class MyThread extends Thread{

        /* (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        @Override
        public void run() {
            Bundle b = new Bundle();
            for (int i=0;i<10;i++){
                b.putInt("count", i);
                Message msg = new Message();
                msg.setData(b);
                mh.sendMessage(msg);
                try{
                    Thread.sleep(1000);                   
                }catch(InterruptedException ex){
                    ex.printStackTrace();
                }
            }
        }
    }
    class MyHandler extends Handler{

        /* (non-Javadoc)
         * @see android.os.Handler#handleMessage(android.os.Message)
         */
        @Override
        public void handleMessage(Message msg) {
            Bundle b = msg.getData();
            TextView tv = (TextView)findViewById(R.id.textview1);
            tv.setText("mythread run:" + String.valueOf(b.getInt("count")));
        }
    }
}

 

 

 

分享