为什么要使用Handler
1、UI更新是在主线程(即UI线程,非线程安全)
2、主线程不能执行耗时操作,耗时超过5秒会出现ANR现象
3、子线程无法更新主线程UI
由此产生了Handler消息传递异步机制,子线程负责耗时操作,主线程负责更新UI,Handler充当子线程和主线程之间的桥梁作用;
Handler的一些特点
1、Handler可分发Message对象和Runnable对象到主线程
2、可控制在主线程或者某个线程的某个地方执行
3、可控制在某个具体时间或者延迟时间执行
Handler中一些分发消息的方法
post(Runnable)//立即执行
postAtTime(Runnable,long)//定时执行
postDelayed(Runnable long)//延时执行
removeCallbacks(Runnable r)//从消息队列中移除一个Runnable对象
sendEmptyMessage(int)//发送空消息
sendMessage(Message)//发送Message对象
sendMessageAtTime(Message,long)//定时发送Message对象
sendMessageDelayed(Message,long)//延时发送Message对象
下面先介绍使用Handler的post实现UI更新
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Handler mHandler;
private TextView btnPost, btnPostDelay, textValue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
btnPost = (TextView) findViewById(R.id.btn_post);
btnPostDelay = (TextView) findViewById(R.id.btn_post_dey);
textValue = (TextView) findViewById(R.id.text_value);
btnPost.setOnClickListener(this);
btnPostDelay.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_post) {//立即执行
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
textValue.setText("来自于工作线程post(Runnable)发送到消息队列,在主线程中执行");
}
});
}
}).start();
} else if (view.getId() == R.id.btn_post_dey) {//延迟执行
new Thread(new Runnable() {
@Override
public void run() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
textValue.setText("来自于工作线程post(Runnable)发送到消息队列,在主线程中延迟三秒执行");
}
}, 3000);
}
}).start();
}
}
}
效果图如下:
再介绍使用Message传递消息
传递Message对象需要使用sendMessage方法将对象入队到消息队列中,同时需要在UI线程中重写handleMessage()方法,用来获取从工作线程中传过来的Message对象。
- 对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。我们也不必担心消息池中的消息过多,它是有上限的,上限为10个。Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是调用的Message.obtain()。
Message有一下几个属性
int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。
int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。
Object obj:传递一个任意的对象。
int what:定义的消息码,一般用于设定消息的标志。使用Message类的属性可以直接携带int类型数据,如果要携带其他类型的数据,可以先将要携带的数据保存到Bundle中对象中,然后通过Message类的setData()方法将其添加到Message中,如:
Message msg = Message.obtain(); Bundle bundle = new Bundle(); bundle.putInt("int", 1); bundle.putBoolean("boolean", false); bundle.putByte("byte", (Byte) null); bundle.putChar("char",'a'); bundle.putCharSequence("Stirng","Hello World"); bundle.putFloat("float", 12.3f); bundle.putString("String", "Hello World"); bundle.putDouble("double", 12.3); msg.setData(bundle);
下面是一个小Demo
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private TextView btnPost, btnPostDelay, btnSendMess, textValue;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.arg1) {
case 0:
textValue.setText(msg.obj.toString());
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnPost = (TextView) findViewById(R.id.btn_post);
btnPostDelay = (TextView) findViewById(R.id.btn_post_dey);
btnSendMess = (TextView) findViewById(R.id.btn_send);
textValue = (TextView) findViewById(R.id.text_value);
btnPost.setOnClickListener(this);
btnPostDelay.setOnClickListener(this);
btnSendMess.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_post) {//立即执行
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
textValue.setText("来自于工作线程post(Runnable)发送到消息队列,在主线程中执行");
}
});
}
}).start();
} else if (view.getId() == R.id.btn_post_dey) {//延迟执行
new Thread(new Runnable() {
@Override
public void run() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
textValue.setText("来自于工作线程post(Runnable)发送到消息队列,在主线程中延迟三秒执行");
}
}, 3000);
}
}).start();
}else if (view.getId() == R.id.btn_send){
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.arg1 = 0;
msg.obj = "来自于工作线程sendMessage发送到消息队列,在主线程中执行";
mHandler.sendMessage(msg);//还有其他几种发送空消息和定时、延迟就不一一列举了
}
}).start();
}
}
}
这里需要注意的是Message.obtain()有几种构造函数,可以参考一下引用案例
public class MyThread implements Runnable{
@Override
public void run() {
/*
* 用obtain获取一个消息,会从消息池中取出一个消息(消息池不为null),
* 消息池的消息减1。如果消息池为空,则调用new Message新建一个
*/
/*第一种方式
Message message = Message.obtain();
message.what = 1;
message.arg1 = 2;
message.arg2 = 3;
message.obj = "lin";
handler.sendMessage(message);
*/
/*第二种方式
Message message = Message.obtain(handler);
message.what = 1;
message.arg1 = 2;
message.arg2 = 3;
message.obj = "lin";
//把发送对象target置为当前传入的handler,故不能用sendMessage
message.sendToTarget();
*/
/*
* 第三种方式
* 第一个参数:Handler
* 第二个参数:what
Message message = Message.obtain(handler, 66);
message.arg1 = 1;
message.arg2 = 2;
message.obj = "monkey";
message.sendToTarget();
*/
/*第四种方式
* 第一个参数:Handler
* 第二个参数:what
* 第三个参数:arg1
* 第四个参数:arg2
* 第五个参数:obj
Message message = Message.obtain(handler, 1, 2, 3, "Mary");
message.sendToTarget();
*/
/*第五种方式
* 第一个参数:handler
* 第二个参数:what
* 第三个参数:obj
*/
Message message = Message.obtain(handler, 1, "Rose");
//除此之外还可以用setData传递一些比较复杂的数据,八种数据类型中的数据都可以传递
Bundle data = new Bundle();
data.putStringArray("str", new String[]{"Rose","Jackson"});
message.setData(data);
message.sendToTarget();
}
}
}
到这里Handler的一些基本使用就结束了!
再介绍一下更新Ui的四种方法
以btn_send的点击事件为例
- 方法1.使用handler的post方法(以上已实现)
- 方法2,使用handler发送消息来处理(以上已实现)
- 方法3,使用runOnUiThread
if (view.getId() == R.id.btn_send){
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
textValue.setText("runOnUiThread(new Runnable())更新UI");
}
});
}
}).start();
}
- 方法4,使用View.post()
if (view.getId() == R.id.btn_send){
new Thread(new Runnable() {
@Override
public void run() {
btnSendMess.post(new Runnable() {
@Override
public void run() {
textValue.setText("View.post(new Runnable())更新UI");
}
});
}
}).start();
}
以上四个方法虽然使用方法不同,但查看源码可以知道其实现原理基本都是通过Handler实现的!