图文并茂深入浅出地话你知,Android的Handler究竟是怎

2018-05-19  本文已影响0人  深图

1.前言

网络上关于Handler的介绍已经有太多了,

本文希望能图文并茂简单地介绍一下Handler,让大家更形象地理解并掌握Handler。

2.Handler是什么?

简单地说,Handler是 用来响应和处理消息的工具,所以才叫“Handler”。

例如,如果你想处理别人给你发的消息,通过Handler提供的方法填写你的响应代码就可以实现。

3.Handler都能干些什么?

通过Handler能干的事情:

1.让程序能响应消息;

2.让程序能发送消息;

3.让程序能删除消息;

为了让程序员用起来方便,体验爽快,在发送消息和响应消息上,有想方设法换不同花样进行封装接口。

例如:

1.发送的消息可以指定什么时间才被处理,比如sendMessageAtTime;

2.响应应消息的时候不关心消息值,只想随手写段代码等一下就能自动被调用,比如post(Runnable)。

3.1 响应消息

为了让程序员便捷地开发响应消息的代码,Handler提供了以下方式来响应消息:

给Handler对象指定一个callback函数;

派生Handler类,重载Handler的handleMessage()函数;

给具体的消息捆绑一个callback;

参考下面的“消息响应示意图”:


消息响应示意图

上图中,“消息处理代码1”就是第1中方式中对应callback函数体,所有“Handler丙”对应的消息被处理的时候都会调用“消息处理代码1”。

“消息处理代码2”就是第2中方式中对应的handleMessage()函数体,所有“Handler甲”的消息被处理的时候都会调用“消息处理代码2”。

“消息处理代码3”就是第3中方式中对应的callback响应函数体,只有“消息乙2”被处理的时候才会调用“消息处理代码3”(其实这个callback在实际编码中,形式是一个Runnable类实例,这个后面有介绍)。

为了更好地让大家了解以上方式的差异,接下来对这几种响应方式配合代码介绍一下。

3.1.1 给Handler对象指定一个callback函数

通过在指定的callback函数内编写响应消息的代码来处理消息,例如在构造函数提供一个callback,参考源码:

public class MyMessageCallback implements  Handler.Callback // 自定义一个callback  
{ 
public boolean handleMessage(Message msg)  
    {  
switch (msg.what)  
        {  
case EVENT_WIN_INIT:  
  do something  
break;  
        }  
return false;  
    }  
}  
Handler myMessageHandler =new Handler(new MyMessageCallback()); // 构造时候提供自定义的callback实例  

从以上源码中可以看出,在构造的时候通过传入MyMessageCallback对象实例的方式来响应和处理消息。

3.1.2 派生Handler类,重载Handler的handleMessage()函数

通过在子类的handleMessage()函数内部编写响应消息的代码来处理消息,参考源码:

public class MyHandler extends Handler
    {
        public void handleMessage(Message msg) // 重载处理消息的函数
        {
            switch (msg.what){
                case EVENT_WIN_INIT:
                    do something...
                    break;
            }
        }
    }

从以上源码中可以看出,只需重载andleMessage()函数即可达到响应和处理消息的目标。对应以上代码,如果你想让它响应EVENT_WIN_INIT消息,那么你只需这样就行:

MyHandler handler = new MyHandler();
handler.sendEmptyMessage(EVENT_WIN_INIT);

3.1.3 给具体的消息捆绑一个callback

在发送具体某个消息的时候指定一个callback,待到消息被排队到处理的时候,将调用指定的callback。

这种方式的callback稍微有点特殊,通过程序提供一个Runnable对象实例来实现。例如post(myRunnable);

参考源码:

public class MyMessageCallback implements Runnable
    {
        public void run()
        {
            do something...
        }
    }

    MyMessageCallback myMessageCallback = new MyMessageCallback();
    handler.post(myMessageCallback);

以上代码,handler.post(myMessageCallback)调用后会在消息队列插入一个新消息,
这个新消息被处理的时候将会调用MyMessageCallback下的run函数。

3.2 发送消息

想要响应消息,必须有人使用Handler对象给你发送消息,
发送消息的本质,就是通过Handler对象往消息队列中插入消息。
参考插入消息示意图:


插入消息示意图

注意很重要的一点,别人给你发送消息用的handler对象必须和你用来响应消息的handler对象是同一个!
例如在“消息响应示意图”中,如果想在“Handler甲”里面响应消息,那么发送消息时候,必须使用“Handler甲”进行发送消息。
Handler中发送消息分为post和send两种,例如post(Runnable)、sendMessage(msg)。其实post和send本质上没有什么差异,仅仅是编码上的形式差异而已,通过不同的封装,让程序员在不同需求场合更为灵活和便捷地编写响应代码。
其中,post的方式一般是自己给自己发送消息,例如自己有段代码想过一会再执行,可以post的方式便捷地编写代码实现,参考下面代码:

Handler handler = new Handler();
handler.post(new Runnable()
{
    @Override
    public void run()
    {
        // "do something"
    }
});

Handler发送消息的各个函数就不在此罗嗦,有需要的请查阅相关代码和资料。

3.3 移除消息

既然有插入消息,肯定有时候会遇到移除消息的情景。所以Handler也提供了从消息队列移除消息的功能函数,例如removeMessages()。
参考移除消息示意图:


移除消息示意图

注意,移除消息仅针对某个handler对象的,例如“Handler甲”只能移除自己插入的消息,不能移除“Handler乙”所插入的消息。

4.Handler和线程通信

本文前面就提到,Handler是 用来响应和处理消息的工具,这个说法其实是过于简略,因为文章刚开始并不想让大家牵涉太复杂的细节。
实际上,Handler的设计更重要的一个目的是让程序能简单地实现线程间通信。所以呢,Handler除了可以用来响应本线程的消息,也能响应其它线程发送过来的消息。
参考下面的“线程通过Handler通信示意图”:

线程通过Handler通信示意图
图中,线程2通过访问“Handler甲”对象,发消息给线程1,
线程1收到消息处理的时候实际运行的代码“Handler甲”中的“消息处理代码”,
这段代码的运行实在线程1中发生的,如此,达到了线程2发消息给线程1的目的。
大概说起来就这么简单,实际操作编码的时候,要注意的两点:
*线程2如何能访问“Handler甲”对象
*“Handler甲”对象如何保证能在线程1中运行
规则:Handler关联哪个线程的消息队列,那么响应时候的处理代码就运行在对应的那个线程。
所以,为了保证“Handler甲”对象能在线程1中运行,“Handler甲”必须关联线程1的消息队列。

Handler怎么关联消息队列?

Handler对象关联消息队列的两种方法:
1.创建的时候自动关联所在线程的消息队列;
2.创建的时候指定某个线程的消息队列;
第1种方法,参考下面的代码,在线程1的运行代码中创建“Handler甲”,构造函数不传参数则自动关联线程1的消息队列:

final Thread thread1 = new Thread(new Runnable()  
        {  
            @Override  
            public void run()  
            {  
                createMessageQueue(); //伪代码:创建线程的消息队列...  
  
                // 在线程1中创建Handler甲,使得Handler甲操作的消息队列属于线程1的
                Handler甲 = new Handler()
                {  
                    public void handleMessage(Message msg)  
                    {  
                        if (msg.what == EVENT_JUST_FOR_TEST) {  
                            do something....  
                        }  
                    }  
                };  
            }  
        });  
        thread1.start();  

从上面的代码可以看到,通过在线程1中“new Handler()”的方式创建了“Handler甲”,这样会自动和本线程的消息队列关联上,也就是“Handler甲”的操作都是针对线程1的消息队列。至于线程1的消息队列怎么来的,大家先别管,本文只介绍Handler。

第2种方法,创建Handler的时候必须指定线程1的消息循环进行关联,例如我们在线程2运行体内创建:

inal Thread thread2 = new Thread(new Runnable()  
       {  
           @Override  
           public void run()  
           {  
               Handler甲 = new Handler(<strong>thread1.getlooper()</strong>)  
               {  
                   @Override  
                   public void handleMessage(Message msg)  
                   {  
                       if (msg.what == EVENT_JUST_FOR_TEST) {  
                           this.getLooper().quit();  
                       }  
                       super.handleMessage(msg);  
                   }  
               };  

以上代码,虽然我们线程2运行体内创建了“Handler甲”,但是“Hanlder甲”关联的线程1的消息队列,所以“Handler甲”的消息被处理的时候,实际上是在线程1中运行的。

结尾

Handler暂时介绍这么多,为了更好地理解Handler的作用,大家还需要对Looper、MessageQueue进行了解。

本文同时发布于:https://blog.csdn.net/henysugar/article/details/80271388

上一篇下一篇

猜你喜欢

热点阅读