美文网首页
Android消息机制解析

Android消息机制解析

作者: Cris_Ma | 来源:发表于2017-06-09 18:36 被阅读0次

    Handler发送消息

    Handler在消息机制中,起到发送和处理消息的作用。发送消息通过Handler.sendMessage(Message msg);,处理消息通过覆写handleMessage方法来实现。

    我们就从sendMessage开始,通过源码,一步一步来查看它是怎样工作的。

    发送消息主要有两个系列,post(runnable)和send(msg),代码如下:

        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 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);
        }
    

    仔细查看以上各种发送消息的方法,就会发现,所有的发送消息最终调用的实际上是sendMessageAtTime,而它最终调用了enqueueMessage, 源码如下:

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

    到这里,消息的发送工作已经完成了,最终消息被传递给了queue.enqueueMessage(msg, uptimeMillis);,这里的queue是什么,在sendMessageAtTime其实已经出现了,是一个消息队列,保存的是Handler的成员变量mQueue

    Handler与Looper和MessageQueue的关系

    上面已经看到,msg最终交给了消息队列mQueue,那么我们再看一下mQueue的值是从哪儿来的,找到Handler的构造函数,mQueue就是在这里赋值的:

       public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
        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;
        }
    

    Handler的构造函数主要有以上两种,一个是包含looper对象,一个不包含looper。先看一下不包含looper的方法,可以看到一句mLooper = Looper.myLooper();,在这里获取了一个Looper对象,然后进行一个判断,如果looper为null,丢出异常,异常的内容相信大家都很熟悉了,子线程创建handler的时候,就会出现这个异常,提示我们在创建Handler调用Looper.prepare()方法。

    既然到这里了,干脆去看一下Looper.prepare()做了哪些工作:

        public static void prepare() {
            prepare(true);
        }
    
        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));
        }
    

    在prepare方法中,传入了一个默认值为true的参数,它表示是否允许looper退出。最后执行sThreadLocal.set(new Looper(quitAllowed)); 创建了一个looper对象,并将它放进了sThreadLocal。

    关于ThreadLocal,它是一个数据存储类,会为每个线程存储并管理单独的数据,各个线程互不影响。到这里就已经很清楚了:

    • 在子线程新建不含looper的Handler对象时,需要先调用Looper.prepare(),为当前线程创建一个looper 对象(每个线程只允许一个looper)。
    • 子线程新建的Handler可以与指定的looper关联,比如Looper.getMainLooper,获取主线程looper。

    现在,了解了looper之后再回到Handler,下一步是通过looper来获取MessageQueue对象:

    mQueue = mLooper.mQueue;

    再到looper中看一下,mQueue是怎么来的:

        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    很简单,直接new了一个MessageQueue! 到这里整个发送消息的流程就已经很清楚了:

    • 构建Handler对象时,需要指定looper,如果没有指定,会在当前线程创建Looper对象
    • looper内部包含了一个消息队列MessageQueue对象
    • handler发送的消息最终被传递给了与handler关联的looper内部包含的MessageQueue

    消息的接收与处理

    通过上边的分析,我们已经看到,msg最终到了queue.enqueueMessage(msg, uptimeMillis);,看一下源码:

     boolean enqueueMessage(Message msg, long when) {
        ......
                 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;
                } else {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
            ......
    }
    

    代码有点长,上边只贴了关键部分,它的作用就是将msg添加到了链表中。到这里消息发送的过程整个就结束了,下一步是又该looper上场了。在looper中有一个loop方法:

    public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
    
            // Make sure the identity of this thread is that of the local process,
            // and keep track of what that identity token actually is.
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // This must be in a local variable, in case a UI event sets the logger
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long traceTag = me.mTraceTag;
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
             ......
                msg.recycleUnchecked();
            }
        }
    

    只截取了关键部分,首先,这是一个死循环,如果消息队列中没有消息,就处于阻塞状态,会一直重复执行(所以调用loop()语句后面的代码不再执行),直到有消息到来,取出消息,调用msg.target.dispatchMessage(msg);,这里的Target就是发送消息的Handler,最终又把消息传回了Handler,并调用了Handler的dispatchMessage方法。当所有的消息都处理完之后,调用return结束loop。

    如果接收消息的looper处于子线程,需要手动调用looper.loop(),否则消息可以发送,但不会取出,也就无法处理消息了。主线程是默认包含looper,也调用了looper.loop()方法的。

    看到这里整个消息机制基本已经结束了,也就明白了异步消息是怎样实现的:

    • Handler负责发送消息,将消息交给关联的looper
    • looper是可以指定的,当前线程的handler可以与任意其他线程的looper关联(通常是主线程)
    • 消息的处理是在looper的loop方法内调用handler的DispatchMessage方法,所以,looper在哪个线程,消息的处理就在哪个线程
    • 子线程不会自动调用looper.loop(),需要手动调用,否则不会响应消息
    • 消息队列处理完毕之后,loop会自动结束,再次发送消息需要继续调用loop方法

    不同类型消息的处理

    在消息机制中可以看到,Handler发出的消息,最终交给了dispatchMessage(Message msg)方法来处理,源码如下:

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

    消息的分发机制如下:

    1. 首先根据接收到的msg判断,如果msg包含callback,就调用handleCallback(msg)方法,Message所包含的callback是哪儿来的呢?

    在看一下发送消息的post方法:

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

    传入了一个Runnable参数,然后又通过getPostMessage将Runnable对象包装成了一个Message:

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

    这个方法将post发送runnable对象包装到一个Message里,Handler收到Message以后会进行判断,如果收到的Message包含Runnable对象(msg.callback!=null),就会调用handleCallback(msg),源码如下:

        private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    直接调用了Runnable对象的run()方法,到这里post方法发送的消息已经处理完毕了。

    1. 第二步检查mCallback != null,这个mCallback是哪儿来的呢?

    再看一下Handler的构造函数就明白了,Handler最终调用的构造函数里包含了一个Callbcak参数,这是Handler的一个内部接口,可以使用它来构建Handler,方法如下:

    Handler n= new Handler(new Handler.Callback(){
    
      @Override
      public boolean handleMessage(Message msg) {
          return false;
      }
    });
    

    通过这种匿名内部类的方式来创建的Handler会执行这一分支,调用mCallback.handleMessage,也就是我们定义Handler时候重写的callback了。

    1. 如果以上两步都不符合,也就是说接收到的消息是通过send方法发出来的,这是最常用的方法,直接调用handleMessage,就是创建Handler时候覆写的handleMessage方法了。

    到这里整个消息机制,Handler的使用解析完毕了。

    这里是一个使用handler的例子,作用是按下Back键时弹出Toast,提示2s内再按一次退出:

     private Handler mHandler;
     private  boolean preQuit;
    
     protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_fragment_container);
            mHandler = new Handler(){
            @Override
                public void handleMessage(Message msg) {
                 switch (msg.what){
                     case 1:
                         preQuit = false;
                         break;
                     default:
                         break;
                   }
               }
       }
    
        @Override
        public void onBackPressed() {
            if (!preQuit) {
                preQuit = true;
                Toast.makeText(LandViewActivity.this,"退出程序请再一次按返回键~",Toast.LENGTH_SHORT).show();
                mHandler.sendEmptyMessageDelayed(1,2000);
            }
            else {
                finish();
            }
        }
    

    当然也可以通过不同的方式来实现,post Runnable对象方式:

     private Handler mHandler;
     private  boolean preQuit;
     Runnable r;
     protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_fragment_container);
            mHandler = new Handler();
           r = new Runnable() {
                @Override
                public void run() {
                    preQuit = false;
                 }
            };  
       }
    
        @Override
        public void onBackPressed() {
            if (!preQuit) {
                preQuit = true;
                Toast.makeText(LandViewActivity.this,"退出程序请再一次按返回键~",Toast.LENGTH_SHORT).show();
                mHandler.postDelayed(r,2000);
            }
            else {
                finish();
            }
        }
    

    使用匿名内部类的方式与第一种方法基本一致,只是创建Handler的时候在Callback参数内重写handleMessage方法:

    mHandler = new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                    preQuit = false;
                    return true;
                }
            });
    

    Handler使用方法

    下面是几个进程之间使用Handler通信的例子:

    1. 子线程发送消息给主线程:
    final Handler mHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    Log.d("HandleMsg", "Receive Msg: No. "+msg.arg1 + " Current Thread: " + Thread.currentThread().getName());
                }
            };
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message msg = mHandler.obtainMessage();
                    msg.arg1 = 1;
                    msg.sendToTarget();
                }
            }).start();
    

    Handler在主线程创建,会自动关联主线程的looper对象,消息在子线程发出之后,会传递到主线程进行处理。

    当然我们也可以用getMainLooper的方式来获取主线程looper:

    new Thread(new Runnable() {
              @Override
              public void run() {
                  Handler mHandler = new Handler(getMainLooper()){
                      @Override
                      public void handleMessage(Message msg) {
                          Log.d("HandleMsg", "Receive Msg: No. "+msg.arg1 + " Current Thread: " + Thread.currentThread().getName());
    
                      }
                  };
                  Message msg = mHandler.obtainMessage();
                  msg.arg1 = 2;
                  msg.sendToTarget();
              }
          },"Thread#2").start();
    

    虽然Handler是在子线程创建的,但是它绑定的looper处于主线程,消息的处理也是在主线程。

    1. 子线程发送消息给当前子线程:
    new Thread(new Runnable() {
          @Override
          public void run() {
              Looper.prepare();
              Handler mHandler = new Handler(){
                  @Override
                  public void handleMessage(Message msg) {
                      Log.d("HandleMsg", "Receive Msg: No. "+msg.arg1 + " Current Thread: " + Thread.currentThread().getName());
                  }
              };
              Message msg = mHandler.obtainMessage();
              msg.arg1 = 2;
              msg.sendToTarget();
              Looper.loop();
          }
      },"Thread#2").start();
    

    这时候handler与子线程的looper管理,子线程是不会自动创建looper的,所以需要Looper.prepare(),发送消息之后还要 Looper.loop();

    1. 主线程发送消息给子线程:
       protected void onCreate(@Nullable Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_container);
    
        MyThread myThread = new MyThread();
        myThread.start();
        Message msg = myThread.getHandler().obtainMessage();
        msg.arg1 = 3;
        msg.sendToTarget();
    
    }
    
     private class MyThread extends Thread{
            private Handler mHandler;
            private final Object mSync = new Object();
    
            public  Handler getHandler(){
                synchronized (mSync) {
                    if (mHandler == null) {
                        try {
                            mSync.wait();
                        } catch (InterruptedException e) {
                        }
                    }
                    return mHandler;
                }
            }
            @Override
            public void run() {
                Looper.prepare();
                synchronized (mSync) {
                    mHandler = new Handler() {
                        @Override
                        public void handleMessage(Message msg) {
                            Log.d("HandleMsg", "Receive Msg: No. " + msg.arg1 + " Current Thread: " + Thread.currentThread().getName());
                        }
                    };
                    mSync.notifyAll();
                }
                Looper.loop();
            }
        }
    

    onCreate方法中获取到子线程的Handler,然后在主线程发送消息,子线程的handler定义时绑定的是子线程的looper,所以消息的处理是处于子线的。

    相关文章

      网友评论

          本文标题:Android消息机制解析

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