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