Handler知识点详解

时间:2022-02-14 17:21:15

Handler是在多线程之间使用的,用于线程之间进行通信。

要想知道为什么需要Handler就首先说明android的主线程和工作线程。
主线程又称为UI线程。正是因为在android中,所有与UI有关的操作(例如创建UI,刷新UI,处理UI等)都必须在主线程中进行,所以主线程又称为UI 线程。
如果当一个程序进行下载等操作的时候,需要的时间是非常长的,这时候如果主线程在执行下载量大的工作时,就会被阻塞在当前状态。此时,就会忽略用户操作,我给他称为假死机(个人理解)。这是如果用户的操作在5秒之内没有得到相应,android系统就会弹出一个对话框—程序无响应,强制关闭当前程序还是等待。这个叫ANR(android not responding)。
这种情况是android应用程序中非常避讳的。
要解决这种问题,就需要程序再创建一个子线程,即工作线程。让工作线程进行耗费大量时间的操作,而让主线程响应用户的操作。
也就是要做到不在主线程中执行可能导致阻塞的耗时代码而是把耗时代码在工作线程中执行。主线程只负责程序的调度和控制

为什么要使用Handler?

有人可能会问:创建一个工作线程太容易了,直接new Thread()不就可以了吗?然后把下载的工作全部放在工作线程中,当下载完成之后更新视图。
其实,这样做是行不通的,因为上边已经说到,android中关于UI的所有操作都必须放在主线程中进行操作。
所以正确的做法应该是:当我们需要下载一幅图片的时候,要创建一个工作线程进行下载,以便于主线程继续响应用户的操作。当工作线程将图片下载完成之后,我们需要工作线程通过一定的消息传递机制将下载的图片传递回UI线程,然后UI线程做显示图片的操作。
Handler,Looper,Message等一些类就凸显出他们的作用。
  Handler知识点详解                            
通过上边的图,我们可以看到,在多线程处理中,在Handler,Message,Looper这三个类的处理中,可以组成一个闭合的环,这样我们就可以完成多线程的操作。

最简单的实现步骤是什么?

首先声明:以下步骤只是最简单的handler的使用步骤,如果想用更深入了解还得继续看下边的内容。
1、在主线程中创建一个Handler对象,并且覆写其handleMessage方法。
2、在创建的工作线程中创建一个Message消息对象,设置其响应的属性
3、使用此handler对象将这个Message发送到主线程的消息队列中。
4、主线程中的Looper对象会不断地监听消息队列中的内容,当监听到消息队列中有新的内容时,就会从消息队列中拿到此消息,并且交给发送这个消息的handler对象进行处理。
5、具体处理的方法则是调用第一步骤中的handleMessage方法中的内容
当看完以上的步骤,也许会有很多疑问,比如Looper是哪里来的?Message是什么?Message中都有哪些内容?
要想了解以上东西,继续往下看。

重点类的重点方法和属性有什么?Looper类

1、作用:
这个是用来封装消息循环和消息队列的一个类。上边的步骤已经说过,这个Looper类负责从消息队列中获取消息,并且执行handler的handleMessage方法。
其实当Looper获取到消息对象后,会进行一系列的判断。可能会执行不同的代码。执行handler的handleMessage方法只是其中的一种可能性。
具体要执行那个方法,我们待会再讲。
2、特性:
       只有在主线程中android系统才会默认的生成一个Looper对象。
       在工作线程中,如果不手动的建立一个Looper对象,默认是不存在Looper对象的。

Message类

1、作用:
         message,顾名思义,就是消息。也就是你要传递的数据的一种载体。例如,我想要发送一个标识符为1,传递一个int类型的数值4和一个代表音乐的music对象。那么我可以设置代表标识符的属性what的值为1,代表int类型的属性值为4,代表Object的属性值为music。
2、构造方法:Message()
3、实例化方法:
   obtain()     
此方法是首先从消息池中取得消息,如果消息池中的消息使用完再新建一个消息,这种方法比直接调用构造方法创建一个新的消息对象节省资源。
   obtain(Message msg)
   obtain(Handler target)
   obtain(Handler target,Runnable callback)
   obtain(Handler target,int what)
   obtain(Handler target,int what,Object obj)
   obtain(Handler target,int what,int arg1,intarg2)
   obtain(Handler target,int what,int arg1,intarg2,Object obj)
4、主要属性
   公共:
   intwhat   代表消息的标识符,用于唯一标识消息类型
   intarg1   可以用此属性传递一个int类型的数值
   int arg2      同arg1
   Object obj         可以用此属性传递一个Object类型的数值
  私有属性
   Handler target:该属性执行消息对象的发送者(handler)
   Runnable callback:
         当调用带有Runnable类型的构造方法创建消息的时候,当Looper对象从消息队列中拿出此消息时,会直接调用实现Runnable接口的对象的run()方法进行处理。而不是调用handler对象的handleMessage方法进行处理
5、主要方法:
   Runnable getCallback()
         获取实现Runnable接口的callback对象,注意这个callback属性没有setter方法,只有getter方法
   void setTarget(Handler target)
         设置目标handler,也就是说设置Looper对象获取这个消息后会交给哪个handler对象进行处理。
         注意,如果一个消息的目标handler为handler1,而实际发送这个消息的是handler2。
那么处理这个对象的handler是handler2,而不是handler1。因为在handler对象发送message的时候,会将发送此message的handler设为目标handler。
   Handler getTarget()
   void setData(Bundle data)
         在message中也可以直接传递Bundle对象,因为Bundle对象中装的都是键值对,这样这个消息就可以承载很多键值对了
   Bundle getData()
   void sendToTarget()
         将此消息发送给目标handler进行处理。也就是说当Looper从消息队列中获取此message后会交给目标handler处理。
         注意,此方法只有在调用setTarget()方法设置目标handler之后才能调用。否则只能使用handler.setMessage()方法发送消息。

Callback

boolean  handleMessage(Message msg)
callback接口中仅仅包含这一个抽象方法。
最值得注意的是,这个handleMessage方法的返回值是boolean类型的,而handler的handleMessage是void类型的。

Handler

1、构造方法:
   Handler()
   Handler(Looper looper) 创建一个Handler对象的时候指定一个Looper对象
   Handler(Callback callabck) 创建一个Handler对象的时候指定一个Callback对象
   Handler(Looper looper,Callback callback)
2、重要私有属性
   Looper looper
   Callback callback
3、主要方法:
   Looper getLooper()
         获取Looper对象。
         注意Looper对象没有setter方法。只能在调用构造方法的时候传递一个Looper对象
   boolean sendMessage(Message msg)
         发送一个message对象
   boolean sendMessageAtFrontOfQueue(Messagemsg)
         发送一个message对象,并且让此对象处于消息队列的头部,首先会被Looper对象拿走
   boolean sendMessageAtTime(Message msg,longtime)
         在一个时间点发送message对象
   boolean sendMessageDelayed(Message msg,longdelayMillis)
         延迟多长时间之后发送message消息
   boolean sendEmptyMessage(int what)
         发送一个只带有消息标识符的空消息
   boolean sendEmptyMessageAtTime(int what,longtime)
   boolean sendEmptyMessageDelayed(intwhat,long delayed)
   boolean post(Runnable callback)
         发送一个带有callback属性的消息
         这个方法的效果相当于
         Messagemessage  = Message. obtain(Handlertarget,Runnable callback)
         handler.sendMessage(message);
         上边的方法只是对以上两句代码的封装
         值得注意的是,并不是将一个实现Runnable接口的对象发送到消息队列,而是把一个封转好的消息发送到消息队列。我记得某个比较出名的视频里边就讲错了。特此更正一下。
   boolean postAtFrontOfQueue(Runnablecallback)
   boolean postAtTime(Runnable callback,longtime)
   boolean postDelayed(Runnable callback,longdelayed)
4、Handler的特性:
  1)Handler对象在创建时必须被关联到一个Looper(关联到消息队列)
    如果构造时未明确指定handler对象关联的Looper
    则默认关联到其创建时所在线程的Looper,如果该线程中不存在Looper
    则Handler创建失败
2)Handler对象,只能向其关联的消息队列发送消息
  3)消息处理原则:
     判断消息中是否包含Runnable对象(m.getCallback()!=null)
     如果消息中包含Runnable对象,则调用该对象的run方法,消息处理结束
     如果不包含Runnable对象,则进一步判断消息的发送者(m.getTarget())
     中是否包含callback对象(m.getTarget().getCallback()!=null)
       如果包含callback,则调用callback的handleMessage方法进行消息处理
         如果callback的handleMessage方法返回trueze消息处理结束
         否则,调用handler的handleMessage方法,消息处理结束
       如果不包含callback,则直接调用handler的handleMessage方法处理,消息
       处理结束

创建消息和发送消息一共有多少种方法?创建消息一共有三种方法:

1、  调用Message的构造方法
2、  调用Message的obtain()方法
3、  调用Handler对象的obtainMessage()的方法

发送消息一共有四种方法:

1、  调用Handler对象的sendMessage方法
2、  调用Handler对象的sendEmptyMessage方法
3、  调用Handler对象的Post方法
4、  调用Message对象的sendToTarget方法

消息处理的逻辑顺序是什么?

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }