为什么要用Handler
在java中我们常用多线程来处理问题,android也会遇到一些耗时长的操作,比如查询数据库,根据查询的结果更新UI,如果放在主线程中就会导致等待时间很长,用户的体验会很差。
android实现子线程的方法和java中一样,新建类继承Thread,新建类实现Runnable接口,或者内部类实现该接口,最常用的就是第三种
这里插入一个代码片
public void onClick(View v){
switch(v.getId()){
case R.id.button:
new Thread(new Runnable(){
@Override
public void run(){
text.setText("hello world!");
}
});
}
}
这就是在子线程中更新ui,如果运行就会抛出异常,因为android不允许在子线程中更新ui,那么要根据子线程的结果更新Ui,我们就需要用到android中的异步消息处理机制:handler
handler可以大致分为四个部分
1.message:被传递的对象,为了分清是谁传递的,它带有what字段作为标记
2.handler 用于发送和处理消息,发送消息会调用sendMessage()方法,处理消息会用到handleMessage()方法。
3.MessageQueue消息队列,存放message,每个线程只会有一个MessageQueue对象。
4.Looper 相当于消息队列的管家,调用Looper的loop()方法后,就会进入到无限循环中,每当消息队列中存入一条消息就会将它取出,传递给handleMessage,每个线程只会有一个Looper对象。
那么最后整理一下如何实现根据子线程的结果更新UI:
首先新建Handle对象,重新handleMessage方法,在子线程需要更新UI时,就新建一个message对象,调用sendMessage将数据发送给主线程,更新UI
子线程
public void onClick(View v){
switch(v.getId()){
case R.id.button:
new Thread(new Runnable(){
@Override
public void run(){
Message message=new Message();
message.what=1;
handler.sendMessage(message);
}
});
}
}
主线程
private Handler handler =new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case 1:
//更新UI
break;
default:
break;
}
}
}
handler也常用于循环发送查询
举个例子,视频的播放进度时长显示,textview需要隔一段时间查询播放器,更新文本内容显示当前播放的时间。这时候就可以在handmessage中调用handler.sendmessageDelay每隔多少秒发送消息给自身达到循环的目的
handler发送消息的方法
分为两个大类,send和post,调用send就是发送message对象,post就是发送Runnable对象(内部类实现),这里post的Runnable对象是UI线程,并不会创建真正的子线程,注意handler定义要在主线程中定义,因为handler默认是使用定义所在线程的looper,所以如果在子线程中定义无参数的handler就会报错:RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
消息的优先级
messageQueue内部的单元肯定是有序的,根据优先级的高低进行排序,这个优先级就是message#when,Message#when 是一个时间,用于表示 Message 期望被分发的时间,该值是 SystemClock#uptimeMillis() 与 delayMillis 之和。
子线程创建handler
之前又提过无法在子线程中直接创建无参数的handler如果业务需求在主线程中发消息给子线程让它进行,那么就需要在子线程中创建handler,如果无参数构造就会自动传入当前线程的looper,但android只会自动创建主线程的looper,所以就会报上面的错误,因为在子线程中并没有启动相应的looper,如果想在子线程中使用handler就需要调用looper.prepare创建looper,然后拿到实例或者调用Looper.myLooper获取当前线程的looper实例,最后调用looper.loop启动循环。注意Looper.loop()这个方法是无限循环的,所以在Looper.loop()后边的程序代码块是无法执行到的,如果传入的是getMainLooper,实际上执行的还是在主线程中,就是一个线程绑定一个looper。