Handler简介及其用法
前言
Handler类主要用处
1.在新启动的线程中发送消息。
2.在主线程中获取并处理消息。
Handler的运行机制
在启动了一个应用后,会有一个主线程,也称作UI线程,只有这个线程才能改变UI界面,伴随着这个线程产生的还有一个Looper,一个Looper可以和多个Handler绑定,一个UI线程就对应着它自己的Looper,这个Looper对象会一直从主线程的MessageQueue(消息队列)中取出消息并传递给Handler进行处理。因此我们在主线程中可以直接创建Handler然后使用即可。一个线程可以拥有多个Handler.
若想在子线程中控制UI界面,我们需要做的就是将主线程的Handler传入,然后向主线程的Handler发送消息,然后在主线程中的handleMessage()中去处理消息。
由于Handler是需要和线程绑定在一起的,因此在初始化Handler的时候需要注意了一般有两种方法创建:
1.给Handler指定Looper对象,那么这个Handler便绑定到了Looper对象所在的线程中,Handler的消息处理回调会在那个线程中执行。可以通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。
2.不指定Looper对象,那么这个Handler绑定到了创建这个线程的线程上,消息处理回调也就在创建线程中执行.
private class MyThread extends Thread {
public void run() {
Looper.prepare();
private Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
...
}
Looper.loop();
}
}
}
这样定义了之后,如果在主线程中new MyThread(), MyThread中的Handler就会和主线程中的looper绑定在一起了,理所当然的handlerMessage()方法也会在主线程中执行,再来控制UI界面就没问题了。
下面有个小例子,用定时任务创建的线程定时任务(其中有子线程)向主线程的Handler发送消息,主线程来进行处理。
public class MainActivity extends Activity {
int[] imageIds = new int[]{
R.drawable.ic_launcher,R.drawable.e1,R.drawable.e2,
R.drawable.e3,R.drawable.e4,R.drawable.e5,
R.drawable.e6
};
int currentImageId = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView imageView = (ImageView)findViewById(R.id.imageview);
final Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//接受消息并更换图片
if (msg.what == 0x1233) {
imageView.setBackgroundResource(imageIds[currentImageId++ % imageIds.length]);
}
}
};
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//向主线程中Handler实例发送消息
myHandler.sendEmptyMessage(0x1233);
}
},0,1200);
}
}
Handler及Loop、MessageQueue之间的合作关系
上面简单的介绍了Handler及其用法之后,下面来介绍下和Handler一起工作的几个组件:
1.Message: Handler接受和处理的消息对象。
2.Looper: 每个线程中只能拥有一个Looper,但是一个Looper可以和多个线程的Handler绑定起来,也就是说很多个线程可以往一个Looper所持有的MessageQueue中发送消息。这就给我们提供了线程之间通信的可能。
3.MessageQueue : 消息队列,采用着先进先出的管理方式,管理Message。
为什么说Looper持有MessageQueue呢?让我们来看看内部的实现:
<span style="font-size:14px;">private Looper () {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}</span>
在初始化Looper的时候,里面会关联着一个MessageQueue实例,这个Messagequeue也就是在线程发送的消息的容器。仔细观察上面的代码,发现Looper是private的,这就说明了我们不能用构造方法来创建Looper对象,那该怎么弄呢?由于和线程扯上了关系,我们不得部分下面两种情况来考虑了:
1.在主线程(UI线程)中,系统在一个应用开始的时候就已经帮我们初始化好了属于主线程的Looper,我们直接可以在主线程中实例化一个Handler对象,使用即可:
<span style="font-size:14px;">Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x1233) {
System.out.println("hello world~~~");
}
}
};</span>
2.第二种情况就是在我们自己启动的子线程中了,自己创建的东西由于不是亲生的(系统自动生成并启动的),所以你懂的,只能靠自己来弄Looper对象了,好在Looper类留了一个静态的方法prepare()供我们使用,可以调用这个方法来创建Looper对象:
<span style="font-size:14px;">public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}</span>
那么还没完呢,有了这个Looper对象之后,怎么让它不断地从自己的MessageQueue中取出消息并传递给绑定了的Handler上面呢?Looper中还有一个死循环的函数,loop(),一旦开始,就会不停的从MessageQueue中取出消息并交给绑定了的Handler处理。网上找了找,下面贴上实现的代码:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static final void loop() {
Looper me = myLooper(); // 线程中存储的Looper对象
MessageQueue queue = me.mQueue; // Looper类中的消息队列
while (true) {
Message msg = queue.next(); // might block 获取消息
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg); // 利用 Target 注册的方法处理消息
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
到这里差不多基础的东西就介绍完了,其是我知道的也就这么多了,,全部都呈上来了,下面总结下关于Handler的一些说法:
1.一个线程只能有一个looper,或者没有looper,这个looper可以是自己通过Looper.prepare()创建出来的,也可以是通过Looper.getMainLooper()得到的.
2.一个Handler只能和一个Looper绑定起来,但是一个Looper可以被多个Handler来绑定,共同使用里面的消息队列.并不会出现冲突的情况,谁发的消息,到时候就该谁拿走.并且处理掉.
3.一个Thread能够有多个Handler,这些Handler如果在创建的时候没有指定Looper,那么就是用的该Thread的Looper,如果指定了Looper,发送的消息将送到指定的looper里面.
4.Handler它用的Looper之间没有任何的瓜葛,只是借用了一下Looper而已,在用mHandler.sendEmptyMessage()的时候,发送到哪个Looper的MessageQueue,到时候就从那个MessageQueue中取出.
5.子线程中想控制UI的话,如果子线程和主线程在同一个文件下(一般都是这样子),直接对主线程的Handler发送消息,如果不在同一个文件夹下,那就将主线程的Handler作为参数传入.好像没有其它的办法,因为即使用同一个Looper,也不能处理其他Handler发送过来的消息,所以在子线程中用Handler发送消息,主线程上的Handler是无法处理的.关于这方面的知识,我还会整理之后和大家分享的,其实在handler向Looper发送消息的时候,传过去了一个target,用来指名这条消息归哪个handler处理的.
今天的博客就到这里咯,如果有什么不对的地方还望各位指出,今天捋Handler的知识把自己都弄晕了......23333