为了解决多线程并发操作UI导致的线程安全问题,Android规定只允许在UI线程中修改UI,子线程若想修改UI可通过Handler机制解决。
Handler像是主线程的一个助手,负责处理其他子线程的消息(请求主线程更新UI),因为主线程很忙,不能即时处理,便让Handler将请求都放在一旁(消息队列里),主线程每隔一段时间就派Looper从消息队列取消息,并交给相应的Handler,Handler根据消息决定如何更新UI。 Handler处理机制
子线程调起Handler发送Message【1】至MessageQueue【2】。Message存储着修改UI用到的信息,MessageQueue是一个消息队列,缓存着多个Message。
主线程自动管理Looper,通过Looper定期轮询消息队列,取出Message【3】,分发给相应的Handler【4】。
Handler收到分发到的消息,进行处理,调用handlerMessage()方法【5】修改UI。
在使用中,我们可以通过重写handlerMessage方法来填写业务代码,如何根据接收到的Message来修改UI就是自己程序的业务了。
如果调用不带参构造器:Handler handler = new Handler(),那么这个handler会默认使用当前线程的looper。
而我们知道只有主线程可更新UI,所以如果想在子线程更新UI,可以选择在主线程无参实例化,或者选择在子线程通过Handler handler = new Handler(Looper.getMainLooper()) 实例化。
使用handler更新UI有多种方式:
public class MainActivity extends Activity {
private TextView mTextView;
//override handlerMessage
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0) {
mTextView.setText("...");
} else if (msg.what == 1) {
mTextView.setText("###");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//child thread
new Thread(new Runnable() {
@Override
public void run() {
Message msg = mHandler.obtainMessage();
msg.what = 1;
mHandler.sendMessage(msg);
}
}).start();
}
}
public class MainActivity extends Activity {
private TextView mTextView;
//override handlerMessage
Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//child thread
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
mTextView.setText("###");
}
});
}
}).start();
}
}
关于handler.post方法再说明一下,不同于第一种方法,调用post会将一个重写的Runnable封装成Message,调起Handler机制处理Message。主线程Looper去消息队列取出Message,调起Runnable的run方法更新UI。