美文网首页
Android----Handler(HandlerA发送的消息

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

作者: 海盗的帽子 | 来源:发表于2019-01-01 17:49 被阅读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 能接收的。

    相关文章

      网友评论

          本文标题:Android----Handler(HandlerA发送的消息

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