Android----Handler(HandlerA发送的消息

2019-01-01  本文已影响6人  海盗的帽子

一.前言

在之前的两篇文章

Handler 消息机制

Handler 消息机制(进阶篇)

中介绍了 Android 中的 Handler ,Looper 和 MessageQueue 的整体工作流程,其中可以知道对于一个 Looper ,可以有多个 Handler 与之绑定(通过 Hanlder 的构造器指定 Looper),那么就产生一个疑问,在同一个线程下,即针对绑定同一个 Looper 的 HanlderA 和 HanlderB ,Hanlder A 发送的消息可以让 HanlderB 接收吗?

答案是:可以的

二.消息的发送

在 Hanlder 消息机制发送消息时指定 Hanlder 主要有下面几种情况。

1.Hanlder 直接发送

            Message message = Message.obtain();
            mHandler1.sendMessage(message);
            //mHandler1.sendMessageAtTime(message,0);
            //mHandler1.sendEmptyMessage(0);
            //mHandler1.sendEmptyMessageAtTime(0,0);
            //mHandler1.sendEmptyMessageDelayed(0,0);
            //mHandler1.sendMessageAtFrontOfQueue(message);

对于类似上面这种方式,从源码中可以看到其实最后都调用了同一个方法
enqueueMessage

  
  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;
        //调用下面的 sendMessageDelayed
       return sendMessageDelayed(msg, delayMillis);
   }

  
   public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
       Message msg = Message.obtain();
       msg.what = what;
             //调用下面的 sendMessageAtTime
       return sendMessageAtTime(msg, uptimeMillis);
   }

  
   public final boolean sendMessageDelayed(Message msg, long delayMillis)
   {
       if (delayMillis < 0) {
           delayMillis = 0;
       }
       //也是调用 sendMessageAtTime
       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;
       }
       
       //最后都是调用 enqueueMessage
       return enqueueMessage(queue, msg, uptimeMillis);
   }

 
   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;
       }
       //这里也是调用 enqueueMessage
       return enqueueMessage(queue, msg, 0);
   }

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
   //注意这里的 
       msg.target = this;
       if (mAsynchronous) {
           msg.setAsynchronous(true);
       }
       //在 enqueueMessage 中调用  queue.enqueueMessage
       return queue.enqueueMessage(msg, uptimeMillis);
   }

从源码中可以看到,对于类是 Hanlder 直接发送的那种方式,最后都会调用 enqueueMessage 方法,而在 这个方法里 ** msg.target = this; ** 这段就是为 消息指定 Hanlder ,这个this ,就是说明 如果 HandlerA 发送了消息,那么 处理这个消息的就是 HanlderA, (这似乎和开头的答案不符,且先保留疑问,继续往下看。)

2.Message sendToTarget

             //第一种,在 obtain 的时候指定 Handler。
            Message message = Message.obtain(mHandler1);
            //Message message = Message.obtain(mHandler1,....);
            message.sendToTarget();

            //或者 使用message.setTarget 指定。
            Message message = Message.obtain();
            message.setTarget(mHandler1);
            message.sendToTarget();

在这种方式下,看 obtain 指定 Hanlder 的情况或者setTarget 的情况。

    public static Message obtain(Handler h) {
        Message m = obtain();
        //注意这里
        m.target = h;

        return m;
    }
    
       public void setTarget(Handler target) {
        this.target = target;
    }
    
    
        public void sendToTarget() {
        target.sendMessage(this);
    }

也是直接将 Hanlder 赋值给 Message 的 Target.
然后再 sendToTarget 中直接使用 Hander.sendMessage 发送这个消息,那么就回到了第一种情况。

从这两种方式可以看出,最后都是会调用 enqueueMessage 中的方法,并指定 HandlerA 发送 HanlderA 就接收。

可以从下面的例子验证

public class Main2Activity extends AppCompatActivity {
    private static final String TAG = "Main2Activity";


    private Handler1 mHandler1 = new Handler1();
    private Hanlder2 mHanlder2 = new Hanlder2();

    class Handler1 extends android.os.Handler {
        @Override
        public void handleMessage(Message msg) {
            Log.e(TAG, msg.what + "Handler1+handleMessage: " + msg.getTarget());
        }
    }


    class Hanlder2 extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Log.e(TAG, msg.what + "Hanlder2+handleMessage: " + msg.getTarget());
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        findViewById(R.id.btn_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                    Message message = Message.obtain(mHandler1);
                    message.setTarget(mHanlder2);//这里只是将 target 换作 hander2 ,实质上还是 Hander2 发送
                    message.sendToTarget();

//                    Message message = Message.obtain();
//                    message.setTarget(mHandler1);
//                    message.sendToTarget();


                    }
                }).start();
            }
        });

        findViewById(R.id.btn_2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message message = Message.obtain();
                message.what = 2;
                message.setTarget(mHandler1);//这里没有用,因为下面发送的代码最后还是会重指定 为 handler2 
                mHanlder2.sendMessage(message);

            }
        });
    }
}

//点击 Button1 
01-01 17:16:54.440 19403-19403 E/Main2Activity: 0Hanlder2+handleMessage: Handler (.Main2Activity$Hanlder2) {ba26787}


//点击 Button2
01-01 17:17:22.303 19403-19403 E/Main2Activity: 2Hanlder2+handleMessage: Handler (.Main2Activity$Hanlder2) {ba26787}

从结果可以看出,不管之前如何设置 target 或则 obtain 方法参数,最后都是只要是 hander2 发送 就 Hander2 接收。

那么答案的可以是如何实现的?其实很简单

findViewById(R.id.btn_2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message message = Message.obtain();
                message.what = 2;
                //和上面的代码不一样的是这两段互换了位置
                //先发送
                mHanlder2.sendMessage(message);
                
                //后指定 target 
                message.setTarget(mHandler1); 

            }
        });

点击 Button2

01-01 17:23:03.157 20678-20678 E/Main2Activity: 2Handler1+handleMessage: Handler (.Main2Activity$Handler1) {b226bc6}

可以看到对于 Hander2 发送的消息,最后回到了 Handler1 这里去执行。
这是为什么?在 enqueueMessage 中最后是调用了 MessageQueue 的 enququMessage

boolean enqueueMessage(Message msg, long when) {
       
        synchronized (this) {
         
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //对于发送的 消息,
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
                //mBlocked 为true 的时候是当消息队列中没有消息的时候
                
            } 
            
            //唤醒 JNI 中的线程
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

我们知道在 Looper 循环获取消息的时候,都是从 MessageQueue 中的next 方法去获取一个消息

      Message next() {
        
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //当没有消息的时候,这里就会阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        // 有消息的时候就不阻塞
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                
                //如果没有消息了 pendingIdleHandlerCount 就不会改动,为 -1
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                
                //那个就会执行这里
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    //Loop 陷入等待。
                    mBlocked = true;
                    continue;
                }

               
        }
    }

从上面可以看出,当执行到 enqueueMessage 这里的时候,会先去执行 native 方法的唤醒操作,而这个时候,已经不是在 java 这里的主线程,因此主线程继续执行,所以就更改了 Message 的 Target ,
然后在分发的时候,因为 target 已经变了,所以就换到 Hander2

      try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

这里的时间间隔其实很短,之后过一段时间再执行 setTarget 就会失效。

            Message message = Message.obtain();
           message.what = 2;
           mHanlder2.sendMessage(message);
           try {
               Thread.sleep(1);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           //这里就无效了
           message.setTarget(mHandler1);

因此,其实 得出结论 HandlerA发送的消息HandlerB 能接收的。

上一篇下一篇

猜你喜欢

热点阅读