[android] Android 一样也有 GUI thread 的问题

[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")));
        }
    }
}

分享