美文网首页androidFloatTaskApp
Android 线程通信之Handler -- 一篇文章彻底弄懂

Android 线程通信之Handler -- 一篇文章彻底弄懂

作者: AndroidTony | 来源:发表于2017-05-22 18:45 被阅读993次

    标签: android 线程 handler


    1 什么是handler

    Handler中文翻译为“处理者”,这里的“处理”顾名思义是在某种条件发生之后所要进行的操作。

    2 handler的作用

    • Handler主要用于异步线程进行通信。这种方式也是Android官方推荐的方式,比较节省性能。我们知道,Android主线程(UI线程)主要负责UI绘制,耗时操作不能放在主线程执行,要不然容易造成卡顿,严重时会ANR。在Android中最常使用Handler的场景就是主线程调用子线程去进行网络访问,子线程在获取到网络访问的返回结果并处理数据之后,通知主线程去更新UI。例如下拉刷新。Handler在这里充当的是消息的发送者和接收者的角色。所谓消息,也就是前面讲的某种特定条件发生之后的状态。例如获取完网络数据之后,获取数据成功或者失败,就是不同的状态,需要发送不同的消息。
    • 另外,Handler还用于延时处理某个任务。

    3 Handler的使用原理

    • Handler既是发送者也是接收处理者,一个消息发送出来,交由哪个Handler去处理,这个完全取决于这个消息的发送者是哪个Handler。即一个Handler发送的消息,只能交由自己去处理。要做到异步线程进行通信,关键在于在A线程中拿到B线程的Handler,拿到之后就可以使用B线程的Handler去发送消息,交由B线程去处理消息了。线程与Handler之间是多对一的关系,一个线程可以有多个Handler,一个Handler只能且必须绑定一个线程。一个线程有多个Handler(使用场景会在后面介绍),用于处理不用类型的消息。
    • 使用Handler的表现形式有两种:
      (1)通过发送消息(sendMessage方法)接收消息(接收消息即回调该Handler的handleMessage方法)并处理的形式;
      (2)通过发送Runnable对象的形式,直接让该Handler所在线程执行其run方法。即调用post(Runnabler)方法。需要注意的是:(1)该Runnable也会被包装成一个消息的形式进行传递。(2)整个过程并没有另外新建线程,该Runnable在Handler所在线程执行。

    4 Handler相关概念

    4.1 Message

    Message用来记录需要传递的信息。该类继承自Parcelable类。
    官方描述为:

    /**

    • Defines a message containing a description and arbitrary data object that can be

    • sent to a {@link Handler}. This object contains two extra int fields and an

    • extra object field that allow you to not do allocations in many cases.

    • While the constructor of Message is public, the best way to get

    • one of these is to call {@link #obtain Message.obtain()} or one of the

    • {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull

    • them from a pool of recycled objects.
      */

    • Message类默认定义了两个int和一个Object(传递对象)用于传输常用的数据,如果不够用,则使用Bundle。

    public final class Message implements Parcelable {
    /**
    * User-defined message code so that the recipient can identify
    * what this message is about. Each {@link Handler} has its own name-space
    * for message codes, so you do not need to worry about yours conflicting
    * with other handlers.
    */
    public int what;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1; 
    
    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;
    
    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     * 
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    public Object obj;
    
    • 另外,还定义了一个target,表示与该Message绑定的Handler。一个callback,当使用post(Runnable r)时,r会被包装成员一个Message,这个Message的callback就是传入的r。

    4.2 MessageQueue

    Handler发送的消息,可以立即处理也可以延时处理,肯定需要一个容器来存放消息。MessageQueue就是这样一个消息队列。

    4.3 Looper

    Message被Handler发送出来,会被放入MessageQueue中,放入其中的Message在条件成熟的时候会被取出来,交由Handler去处理。整个过程中还需要一个对象来执行获取并放入,取出并交给对应Handler的工作。这个工作就由Looper来完成。
    Looper需要执行两个过程:(1)与线程进行绑定prepare方法(2)读取消息队列中的消息并交由Handler进行处理loop方法

          private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
    

    ** 4.4 整个过程

    • 一个线程对应一个Looper、一个MessageQueue以及多个Handler、多个Message
    • 一个Message对象,在被发送的时候将发送该Message对象的Handler对象的引用保存到target成员变量上,looper在轮询Message的时候,就直接把该Message交给message.target这个Handler去处理就可以了。
    • 在调用Handler的sendMessage后,会把Message对象放入一个MessageQueue队列,该队列属于某个Looper对象,每个Looper对象通过 ThreadLocal.set(new Looper())跟一个Thread绑定了。Looper对象所属的线程在Looper.Loop方法中循环执行如下步骤:从MessageQueue队列读取 Message对象,并把Message对象交由Handler处理,调用Handler的dispatchMessage方法。

    5 Handler的使用步骤

    5.1 获取Handler

    • 当前Thread已经有Looper的情况(主线程默认已经在ActivityThread的main方法中初始化Looper),则直接new一个Handler。可以选择重写或者不重写handleMessage方法(不重写将只能使用post的方式调用)。此时该Handler会自动绑定到当前线程。
      官方解释为:

    /**
    * Default constructor associates this handler with the {@link Looper} for the
    * current thread.
    *
    * If this thread does not have a looper, this handler won't be able to receive messages
    * so an exception is thrown.
    */

    public Handler() {
            this(null, false);
        }
    

    /**
    * Subclasses must implement this to receive messages.
    */

    public void handleMessage(Message msg) {
        }
    

    实例:

    public class MyActivity extends BaseActivity{
        private static final String TAG = "MyActivity";
        Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case CommonConstants.MSG_REFRESH_VIDEO_ITEM:
                        doSomething();
                        break;
                    default:
                        break;
                }
            }
        };
    
    • 当前线程还没有Looper的情况,则需要先初始化当前线程的Looper,然后new一个Handler。
      初始化当前Looper的方式为:
      *  class LooperThread extends Thread {
      *      public Handler mHandler;
      *
      *      public void run() {
         /** Initialize the current thread as a looper.
          * This gives you a chance to create handlers that then reference
          * this looper, before actually starting the loop. Be sure to call
          * {@link #loop()} after calling this method, and end it by calling
          * {@link #quit()}.
          */
      *          Looper.prepare();
      *
      *          mHandler = new Handler() {
      *              public void handleMessage(Message msg) {
      *                  // process incoming messages here
      *              }
      *          };
      *
       /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
      *          Looper.loop();
      *      }
      *  }
    

    在初始化Handler之前需要调用Lopper.prepare()方法初始化Looper,初始化Handler之后调用Looper.loop()开始轮询消息。

    • 如果在子线程使用主线程的Handler,可以使用new Handler(Looper.getMainLooper())的方法得到。

    5.2 使用Handler发送消息

    handler发送消息的方法是比较多,常用的就包括
    获取消息包括Runnable对象和message对象两种,获取Message的方式包括:
    1 直接new一个Message,设置what等参数(不推荐)。
    2 通过handler获取复用的Message(推荐)

    • handler.obtainMessage()
    • handler.obtainMessage(int what)
    • obtainMessage(int what, Object obj)
    • ......

    3 直接发送空Message,只有what

    • sendEmptyMessage(int what)

    发送消息的方法:

    post(Runnable)
    postAtTime(Runnable,long)
    postDelayed(Runnable long)
    sendEmptyMessage(int)
    sendMessage(Message) 将消息发送到消息队列
    msg.sendToTarget() 将消息发送到消息队列
    sendMessageAtTime(Message,long) 定时将消息发送到消息队列
    sendMessageDelayed(Message,long) 延迟一定时间后,将消息发送到消息队列

    5.3 使用Handler接收消息并处理

    在Handler的handlerMessage中根据what去处理相应逻辑。

    6 发送Runnable和发送Message的区别

    post方式:发送runnable,实际上是把runnable对象设置为一个Message的成员变量callback之后,发送该Message。最后都是走到sendMessageDelayed方法当中。唯一的区别在于,post方式的Message的callback不是null,send方式发送的Message的callback是null。
    1

     public final boolean post(Runnable r){
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
        
     private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }
    

    2

      public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    

    send方式:
    1

    public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }
    

    2

        public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    

    Looper在处理的时候:如果msg.callback的是null,则直接调用其run方法;如果为空,则交由handlerMessage去处理。
    1

     public static void loop() {
            ...
                try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                ...
        }
    

    2

        /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    7 实例

    实例1:handler定时器
    需求:在主线程每隔一段时间执行一次
    代码:
    1

     private static long mDelayMillis = 1000;
      Handler mHandler = new Handler(Lopper.getMainLooper());
      Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        ...
                        mHandler.postDelayed(this, mDelayMillis);
                };
      mHandler.post(runnable);
      //如果要取消任务
       mHandler.removeCallbacks(runnable);
    

    相关文章

      网友评论

      • shUID:Looper在处理的时候:如果msg.callback的是null,则直接调用其run方法;如果为空,则交由handlerMessage去处理。

        这说的是啥意思?后面那句"如果为空"说的是什么为空
      • Bo动:在实际操作中发现有这么一个问题:
        场景:一个Activity的主线程中有两个Handler(handler1,handler2),分别有自己的handMessage(Messgae msg)方法, 想用handler2发送消息,在handler1中收到,代码大致如下:
        Message msg = Message.obtain();
        msg.setTarget(handler1);
        handler2.sendMessage(msg);
        发现仍旧在handler2的handMessgae方法中获取到了该消息,也就是说msg.setTarget(handler1);这句代码无效。请问这怎么回事?
        Bo动:@云波丽 十分感谢
        AndroidTony:@云波丽 Message的Handler target字段,在setTarget()的时候被赋值为handler1,在handler2.sendMessage(msg)的时候被赋值为handler2,源码在Handler的private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 里面,是这样: "msg.target = this;" 另外,setTarget()是为了能够直接调用Message的sendToTarget()方法,要不然setTarget()没什么意义。

      本文标题:Android 线程通信之Handler -- 一篇文章彻底弄懂

      本文链接:https://www.haomeiwen.com/subject/bjxsxxtx.html