Android线程间通信(四):Handler

  在Android整个线程间通信的结构中,Message是通信内容的载体,MessageQueue是Message的管理者,Looper负责从MessageQueue中循环地取消息并分发给对应Handler处理,而Handler是Message的发布者兼处理者。
  Handler是实现线程间通信的关键,任何从其他线程发往本线程的消息都要通过Handler发送。Handler在创建时会默认绑定本线程的Looper,这是Handler发出的消息能够被本线程接收的关键。


1.创建Handler

  Handler一共有7个构造方法,使用时根据需要指定的参数确定调用哪个构造方法即可。我们先看其中5个只用于简化参数的构造方法:

public Handler() {
   this(null, false); //Handler(Callback callback, boolean async)
}

public Handler(Callback callback) {
   this(callback, false); //Handler(Callback callback, boolean async)
}

public Handler(Looper looper) {
   this(looper, null, false); //Handler(Looper looper, Callback callback, boolean async)
}

public Handler(Looper looper, Callback callback) {
    this(looper, callback, false); //Handler(Looper looper, Callback callback, boolean async)
}
public Handler(boolean async) {
    this(null, async); //Handler(Callback callback, boolean async)
}

&esmp; 这五5个构造方法其实最后都是通过下面两个构造方法完成构造的:

    /**
     * 将这个标识位设置为true,表示程序需要检测Handler的子类中非静态的 匿名子类、局部子类或者成员子类。
     * 这些类可能造成泄露。
     * Set this flag to true to detect anonymous, local or member classes
     * that extend this Handler class and that are not static. These kind
     * of classes can potentially create leaks.
     */
    private static final boolean FIND_POTENTIAL_LEAKS = false;

   /**
     * 构造方法会将Handler和当前线程的{@link Looper}绑定,为Handler设置一个Callback参数以便在处理
     * Message时回调Callback参数的{@link android.os.Handler.Callback#handleMessage(Message)},
     * 并且根据参数async设定Handler是否需要是异步的。
     *
     * 默认的Handler都是同步的,除非构造方法中特别指明构造的Handler需要是异步的。
     *
     * 异步Handler发布的消息都是异步消息。有别于同步消息必须按照顺序处理,异步消息(比如中断消息和事件消息)
     * 并不要求处理时是有序的。异步消息不会被{@link MessageQueue#enqueueSyncBarrier(long)}设置的同
     * 步障碍器干扰。
     *
     * @param callback 用于处理消息的Callback接口实现类对象或者null;
     * @param async 如果为true,Handler会调用{@link Message#setAsynchronous(boolean)}将接受到
     *              的所有{@link Message} 和 {@link Runnable}设置为异步Message。
     *
     * @hide
     */
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {  //是否检测类性质
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    /**
     * 用提供的{@link Looper}对象替代当前线程的Looper与Handler绑定 ,为Handler设置一个Callback参数以
     * 便在处理消息时回调Callback参数的{@link android.os.Handler.Callback#handleMessage(Message)},
     * 并且根据参数async设定Handler是否需要是异步的。
     *
     * 默认的Handler都是同步的,除非构造方法中特别指明构造的Handler需要是异步的。
     *
     * 异步Handler发布的消息都是异步消息。有别于同步消息必须按照顺序处理,异步消息(比如中断消息和事件消息)
     * 并不要求处理时是有序的。异步消息不会被{@link MessageQueue#enqueueSyncBarrier(long)}设置的同
     * 步障碍器干扰。
     *
     * @param looper 与Handler绑定的looper,不能为null;
     * @param callback 用于处理Message的Callback接口实现类对象或者null;
     * @param async 如果为true,Handler会调用{@link Message#setAsynchronous(boolean)}将接受到
     *              的所有{@link Message} 和 {@link Runnable}设置为异步Message。
     *
     * @hide
     */
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper; //不能为null
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

  无论是上述哪个构造方法,在构造时都指定了Handler类用于保存Looper对象的成员变量mLooper值。也就是说在创建Handler时,已经为其绑定了Looper对象,这个对象在Handler发送消息时是必不可少的。同样绑定了的还有消息队列mQueue。
  Callback类型的mCallback和boolean类型的mAsynchronous都是可选的属性。mCallback参见本文第三节: 消息的分发与处理 Callback接口。mAsynchronous默认是false,用于指明Handler是否是异步Handler。同步Handler和异步Handler的不同在于,异步消息发送消息时会把所有消息都设置成异步消息,同步Handler不管传递过来的消息是同步还是异步都不改动。


2.发送/布消息

 &esmp;对外Handler把消息分成了两种:Message和Runnable,发送Message时使用sendMessage()系列方法,发布Runnable时使用post系列方法。在Handler内部Runnable会被封装成简易的消息,之后的操作几乎和Message一致。Runnable的封装如下:

private static Message getPostMessage(Runnable r) {
       Message m = Message.obtain();
       m.callback = r;
       return m;
}

private static Message getPostMessage(Runnable r, Object token) {
      Message m = Message.obtain();
      m.obj = token;
      m.callback = r;
      return m;
}

  sendMessage和post系列方法和Handler的构造方法系列类似,封装了各种参数但是最后真正执行都集中在一两个方法中。我们先来熟悉熟悉sendMessage和post系列方法封装了哪些参数:

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

//在指定时间消息处理时间
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
   return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

//在指定时间消息处理时间
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
   return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

//在指定时间消息处理的延时时间
public final boolean postDelayed(Runnable r, long delayMillis)
{
  return sendMessageDelayed(getPostMessage(r), delayMillis);
}

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

//发送空消息
public final boolean sendEmptyMessage(int what)
{
  return sendEmptyMessageDelayed(what, 0);
}

//发送空消息,并指定延时时间
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
   Message msg = Message.obtain();
   msg.what = what;
   return sendMessageDelayed(msg, delayMillis);
}

//发送空消息,并指定执行时间
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
   Message msg = Message.obtain();
   msg.what = what;
   return sendMessageAtTime(msg, uptimeMillis);
}

//发送消息,设置延时时间
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
       delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//以上所有方法最终都会调用这个方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
   MessageQueue queue = mQueue;
   if (queue == null) {
       RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
       Log.w("Looper", e.getMessage(), e);
       return false;
   }
   return enqueueMessage(queue, msg, uptimeMillis);
}

  可以看到,所有的post方法都会将Runnable封装成消息并调用对应的sendMessage方法实现入队。源代码非常严谨地为所有方法都加上了final关键词,这使得Handler的子类们不能重写这些方法。
  在发送消息时,我们可以根据掌握的资源调用相应的发送方法。需要注意的是执行时间用的并不是日常生活中的时间,而是Android系统自启动开始算起的非深度睡眠时间(称为正常运行时间)。我们发送消息的时候,如果想要指定执行时间就需要调SystemClock.uptimeMillis() 得到当前的正常运行时间,之后再加上需要偏移的延时时间。但不建议这么做,因为提供的API中已经含有丰富的方法能够指定延时时间而非执行时间。

//使消息立即执行
public final boolean postAtFrontOfQueue(Runnable r)
{
  return sendMessageAtFrontOfQueue(getPostMessage(r));
}   

//发送消息,并立即执行
public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
     }
     return enqueueMessage(queue, msg, 0);
}

  需要特别指出的是postAtFrontOfQueue/sendMessageAtFrontOfQueue方法,前者封装好Runnable后会直接调用后者的。通过这两个方法发送的消息执行时间都会被设置成0,在这个消息进入消息队列时,会被直接放入队列的最前面(如果队列没有其他when=0的消息)。这个方法仅用于非常特殊的情况下。 因为它很容易破坏消息队列的循环,导致顺序问题或者其他不可预见的副作用(援引sendMessageAtFrontOfQueue方法注释)。

/**
* Enqueue a message at the front of the message queue, to be processed on
* the next iteration of the message loop. You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* This method is only for use in very special circumstances – it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
———— sendMessageAtFrontOfQueue()注释

  追踪上述方法的调用关系,可以发现最终都会调用enqueueMessage()。这个方法负责把上层方法封装好的消息加上target和同异步标识,并加入Handler对应的消息队列中。其代码如下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
     msg.target = this;
     if (mAsynchronous) {
        msg.setAsynchronous(true);
     }
     return queue.enqueueMessage(msg, uptimeMillis);
}

  在创建Handler时可以指定Handler是同步的还是异步的(保存在mAsynchronous),如果Handler是异步的,在enqueueMessage会将发送的所有消息都设置成异步消息。
  queue.enqueueMessage(msg, uptimeMillis)返回的入队结果会一层一层往上反馈。为了保证消息能够被处理,在发送消息后应该考虑发送失败的情况并制定相应的对策。MessageQueue.enqueueMessage()在Android线程间通信(二):MessageQueue(中) 中已经分功能解读过了,在这里就不再累述。当Handler将消息从其他线程入队到对应的消息队列那一刻,也是消息从其他线程移交本线程的一刻。之后消息的分派、处理和大部分管理操作都在本线程中完成。


3.消息的分发与处理 Callback接口

  Handler把消息发给消息队列后,消息队列就承担起了消息的管理工作。消息队列在合适的时机会将消息传递给Looper;Looper得到消息后能够从消息中得到其对应的Handler对象,并通过该Handler的dispatchMessage()方法将消息分派给Handler处理;Handler从分派方法中得到消息后,能够根据消息携带的参数指定不同的方法来完成消息的处理。

public void dispatchMessage(Message msg) {
   if (msg.callback != null) {
      handleCallback(msg); //函数体为:message.callback.run();
   } else {
      if (mCallback != null) {
          if (mCallback.handleMessage(msg)) {
             return;
          }
      }
      handleMessage(msg);
     }
}

  由上述代码我们可以知道:在分发msg时,callback.run()优先级最高,mCallback.handleMessage(msg)次之,handleMessage(msg)最低。callback.run()是创建callback时需要实现的方法,handleMessage(msg)是创建Handler时需要重写的方法。而mCallback是一个接口,其代码如下:

 public interface Callback {
        /**
         * @param msg {@link android.os.Message Message}对象
         * @return 返回true,如果不需要更深层次的处理。返回false则会继续调用{@link #handleMessage(Message)}
         */
        public boolean handleMessage(Message msg);
}

  Handler的mCallback字段只能在创建Handler时赋值,其效果等同于Handler的handleMessage(msg)。当某个Handler对象发布的消息共享同一个处理程序时,推荐使用Handler对象的handleMessage()方法或者Callback接口来完成消息的处理任务;当一个Handler发布的消息需要不止一种处理程序时,可再结合消息对象中Runnable类型的callback字段。


4.获得Handler对应的Looper

  在默认情况下,哪个线程创建Handler,Handler就与哪个线程的Looper对象绑定。除非在创建Handler时,明确地指定了需要绑定的Looper,这种情况一般出现在非UI线程中指定UI线程的Looper对象与Handler对象绑定。
  当我们需要得到Handler对应的Looper时,调用Looper.getLooper()方法即可。如果需要主Looper,调用Looper类的静态方法getMainLooper()即可。


常用方法列表:
1.public String getMessageName(Message message):返回消息的名称。如果消息的callback 字段不为空,则返回callback的类名称;为空,则返回what字段的十六进制字符串。
2.public final Message obtainMessage()系列 :调用Message.obtain()系列方法从缓存池取出一个消息。
3. public final void removeCallbacks(Runnable r):移除消息队列中所有callback字段为r的消息。
4. public final void removeCallbacks(Runnable r, Object token):移除消息队列中所有callback总段为r,且obj字段等于token或者null的消息。
5. public final void removeMessages(int what):移除消息队列中所有what字段等于what的消息。
6. public final void removeMessages(int what, Object object):移除消息队列中所有what字段等于what,且obj字段等于object或者null的消息。
7. public final void removeCallbacksAndMessages(Object token):移除所有消息队列中obj等于token或者null的消息。
8. public final boolean hasMessages(int what)
9. public final boolean hasMessages(int what, Object object)
10. public final boolean hasCallbacks(Runnable r)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值