Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行,很多人认为Handler的作用是更新UI ,这的确没错,但是更新UI仅仅是它的一个特殊使用场景。有时候我们需要在子线程中进行耗时的I/O操作,可能是读取文件或者访问网络……,当耗时操作完成以后可能在UI上做一些改变,由于Android开发规范的限制,我们并不能在子线程访问UI控件,否则会触发程序异常,这个时候通过Handler就可以将更新UI的操作切换到主线程中执行。因此,本质上来说,Handler并不是专门用于更新UI的,它只是被开发人员用来更新UI。
Handler的运行需要底层的MessageQueue(消息队列)和Looper(消息循环)的支撑,MessageQueue内部存储了一组消息,以队列的形式对外提供插入和删除的工作,消息队列的内部存储结构并不是真正的队列,是采用单链表的数据结构来存储消息列表。Looper在这里可以理解为消息循环。由于MessageQueue只是一个消息的存储单元,他不能去处理消息,而Looper会以无限循环的形式去查找是否有新消息,如果有就处理,否则就一直等待着。Looper还有一个特殊的概念,那就是ThreadLocal,它不是一个线程,它的作用是可以在每个线程中存储数据。Handler创建的时候会采用当前线程的Looper来构造循环系统,Handler内部就是使用ThreadLocal在不同的线程中互不干扰地存储并提供数据,通过ThreadLocal可以轻松获取到每个线程的Looper.
注意:线程默认没有Looper,如果需要使用Handler就必须为线程创建Looper,我们经常提到的主线程就是UI线程(ActivityThread),ActivityThread被创建时就会初始化Looper,这就是在主线程中默认使用Handler的原因。
曾经一道尴尬的面试题:(Handler与Looper的对应关系,一对一,还是多对一)
消息处理类Handler允许发送和处理Message或Runnable对象到其所在线程的MessageQueue中。Handler主要有一下两个作用:
1.将Message或Runnable应用post()或sandMessage()方法发送到MessageQueue中,在发送时可以指定延迟时间、发送时间以及要携带的Bundle数据。当MessageQueue循环到该Message时,调用相应的Handler对象的handlerMessage()方法对其进行处理。
2.在子线程中与主线程进行通信,也就是在工作线程中与UI线程进行通信。
*在一个线程中,只能有一个Looper和MessageQueue,但是可以有多个Handler,而且这些Handler可以共享一个Looper和MessageQueue。
Handler类提供的发送和处理消息的常用方法如下表:
[plain] view plain copy
void handleMessage(Message msg):处理消息的方法,该方法通常用于被重写。
final boolean hasMessage(int what):检查消息队列是否包含what属性指定值的消息。
final boolean hasMessage(int what,Object object):检查消息队列中是否包含what属性为指定且object属性为指定指定对象的消息。
Message obtainMessage():获取消息。
sendEmptyMessage(int what):发送空消息。
final boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒之后发送空消息。
final boolean sendMessage(Message msg):立即发送消息。
final boolean sendMessageAtTime(Message msg,long uptimeMillis):定时发送消息。
final boolean sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒之后发送消息。
二.消息类(Message)简介
消息类(Message)被存放在MessageQueue中,一个MessageQueue中可以包含多个Message对象,每个Message对象可以通过Message.obtain()或Handler.obtainMessage()方法获得。一个Message对象有如下5个属性:
[plain] view plain copy
arg1 int类型 用来存放整型数据
arg2 int类型 用来存放整型数据
what int类型 用来保存消息标示
obj Object类型 是Object类型的任意对象
replyTo Messager类型 用来指定此Message发送到何处的可选Message对象
*使用Message类的属性可以携带int类型数据,如果要携带其他类型的数据,可以先将要携带的数据保存到Bundle中对象中,然后通过Message类的setData()方法将其添加到Message中。
如:
Message msg = new Message();
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);
总之,Message类的使用方法比较简单,在使用时,需注意以下3点:
a.尽管Message有public的默认构造方法,但是通常情况下,需要使用Message.obtaion()或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源。
b.如果一个Message只需要携带简单的int型信息,应优先使用Message.arg1和Message.arg2属性来传递信息,这比用Bundle更节省内存。
c.尽可能使用Message.what来标识信息,以便用不同方式处理Massage。
/**
*
* handler.post(Runnable);
*
*
*/
handler.post(new Runnable() {
@Override
public void run() {
isChanging = true;
bar.setProgress(count);
count++;
}
});
/**
*
*
* hanlder.postDelayer(Runnable,timeDelayed);
*
*/
handler.postDelayed(new Runnable() {
@Override
public void run() {
isChanging = true;
bar.setProgress(count);
count++;
}
},100);
/**
* 重点看一下handler是怎么发送msg的
*
* 1.可以发送空的Message
* 2.可以定时发送Message
* 3.Message可以携带的数据有 int what,int arg1,int arg2,Object obj, Bundle bundler(通过msg.setData(Bundle bundler)来添加到Message对象中)
*
* 4.Message通过Handler.obtain()方法实例化,可以进行有参、无参实例化,其中有参实例化是可以出传入 int what,int arg1,int arg2, Object obj
* @author VicentTung
*
*/
/**
* 1.发送空的Message
*/
handler.sendEmptyMessage(0);
handler.sendEmptyMessageAtTime(0, System.currentTimeMillis());
handler.sendEmptyMessageDelayed(0, 100);
/**
*2. 发送非空Message(Message是new出来的)
*/
Message msg =new Message();
Bundle data_bundle = new Bundle();
data_bundle.putString("name", "小明");
data_bundle.putInt("age", 12);
msg.arg1=1;
msg.arg2=2;
msg.what=3;
msg.obj="success";
msg.setData(data_bundle);
handler.sendMessageDelayed(msg, 100);
/**
* 3.发送非空Message(Message是handler.obtain()出来的)
*/
String obj="hahah";
int arg1=111;
int arg2=222;
Message msg_obtain =handler.obtainMessage();
/**
* 各种各样的带参数的obtain()方法实例化出Message对象
*/
msg_obtain =handler.obtainMessage(what);
msg_obtain = handler.obtainMessage(what, obj);
msg_obtain = handler.obtainMessage(what, arg1, arg2);
msg_obtain =handler.obtainMessage(what, arg1, arg2, obj);
/**
* 4.Message的移除
*/
if(handler.hasMessages(0)){
handler.removeMessages(0);
}