我们在开发应用程序的时候,考虑到线程安全的问题,子线程是不能直接修改UI的,也就是说Android的UI也是不安全的线程,如果想要更新UI元素,则必须在主线程里执行,否则就会出现异常。
首次来看一个在子线程修改UI的例子:
1、新建一个TestDemo项目,然后修改MainActivity.xml中的代码,代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btn_click_me"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="click me"
/>
<TextView
android:id="@+id/tv_show_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="dd"
/>
</LinearLayout>
在布局文件MainActivity中添加了两个控件,一个Button和一个TextView.我希望当我点击Button时能够把TextView的Text属性值修改成我所希望的值。
下面来看一下MainActivity中的代码:
public class MainActivity extends ActionBarActivity implements OnClickListener {
private static final String TAG = "MainActivity";
private Button btn_click_me;
private TextView tv_show_info;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_click_me = (Button) findViewById(R.id.btn_click_me);
tv_show_info = (TextView) findViewById(R.id.tv_show_info);
btn_click_me.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_click_me: {
log("----------------click------------------------");
new Thread(new Runnable() {
@Override
public void run() {
tv_show_info.setText("hello world");
}
}).start();
}
break;
default:
break;
}
}
public void log(String info) {
Log.d(TAG, info);
}
}
这段代码很简单,在btn_click_me按钮的点击事件里面开启了一个子线程,然后在子线程中调用TextView的setText的方法将显示的字符串改成hello world。代码逻辑非常简单,不过这是在子线程中更新UI的。现在运行一下这段代码,你会发现程序崩溃了。查看LogCat中的错误日志,你可以看到造成崩溃的原因是由于在子线程中更新UI所导致的。异常信息如下:
以上可以证实了Android确实是不允许在子线程中对UI元素进行修改。但是Android为我们提供了一套异步消息处理机制,能够完美的解决在子线程中操作UI.现在我们来修改一下MainActivity中的代码。代码如下:
public class MainActivity extends ActionBarActivity implements OnClickListener {
private static final String TAG = "MainActivity";
private Button btn_click_me;
private TextView tv_show_info;
private static final int UPDATA_TEXT=1;
private Handler handler=new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATA_TEXT:
tv_show_info.setText(msg.obj.toString());
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_click_me = (Button) findViewById(R.id.btn_click_me);
tv_show_info = (TextView) findViewById(R.id.tv_show_info);
btn_click_me.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_click_me: {
log("----------------click------------------------");
Message msg=new Message();
msg.what=UPDATA_TEXT;
msg.obj="hello world";
handler.sendMessage(msg);
}
break;
default:
break;
}
}
public void log(String info) {
Log.d(TAG, info);
}
}
在修改后的代码里面新增了一个静态常量UPDATA_TEXT,用来表示更新textView的动作,然后又定义了一个Handler对象,并重写了父类的handlerMessage方法,在重写的方法中进行了对textView的操作。在重写的handlerMessage里面你会发现当msg.what的值等于UPDATA_TEXT时,就会将TextView的值改我msg.obj传递过去的值hello world.