美文网首页
Android 必备知识之Handler机制

Android 必备知识之Handler机制

作者: shengshengboom | 来源:发表于2020-05-15 14:48 被阅读0次

    Handler是我们常用的一种通信方式,可用于子线程更新UI

    对于Handler我们需要知道的有以下四种对象

    • Handler:用于分发消息
    • MessageQueue:用于存储Message
    • Message:通信的消息
    • Looper:一个消息循环机制

    一:Handler的基本使用

    我们使用Handler的时候,一般是这么使用的(有多种使用方式,这里简单的举例)

    // MainActivity.kt
    val handler: Handler = Handler(
      Handler.Callback {
        when (it.what) {
          1 -> {
            // doSomething
          }
          else -> {}
        }
        true         
      }
    )
    
    // 在其他地方调用
    val obtain = Message.obtain()
    obtain.what = 1
    handler.sendMessage(obtain)
    

    先新建一个Handler,并声明回调,在回调中根据传递的数据做不同的动作,之后在任何一个地方都可以使用handler对象的sendMessage()方法,发送数据,数据会在之前声明的回调中接收到

    二:源码分析

    为什么我们的Handler可以这么使用呢?

    对于这个问题,我觉得我们学习Android的有必要知道一下,起码以后面试可能也有用处。

    我们可以一步一步来分析

    首先我们使用Handler就必须创建一个Handler,我们查看Handler的构造函数

    // Handler.java
    
    // 本例用的即此构造函数
    public Handler(Callback callback) {
      this(callback, false);
    }
    
    public Handler(Callback callback, boolean async) {
      ...
      mLooper = Looper.myLooper(); // 1
      if (mLooper == null) {
        throw new RuntimeException(
          "Can't create handler inside thread " + Thread.currentThread()
            + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    此时我们可以看到1位置上Looper.myLooper()这是去获取Looper对象,Looper是一个循环机制,可以不断的从队列中拿到消息进行发送,我们来看下这个对象是怎么获取到的

    // Looper.java
    
    public static @Nullable Looper myLooper() {
      return sThreadLocal.get();
    }
    

    看到这里,可能大家会疑惑这sThreadLocal是什么鬼,其实这是一个ThreadLocal对象,我们可以简单理解下,就是它可以让我们每个线程都有一个属于自己的局部变量,隶属于线程,在当前线程中使用sThreadLocal.get()都可以拿到线程中唯一的局部变量,可能介绍的不是那么容易懂,其实它的源码也很简单,有兴趣的大家可以去看看,绝对让你一下子就能理解的。

    注意:使用Looper也是有规定的,需要先调用Looper.prepare(),然后才能调用Looper.loop(),为什么?请看下面的解释

    一般我们使用Looper,我们如果想要让子线程一直运行,并随时在子线程中处理数据
    则可以这么做

    Thread {
      Looper.prepare()
        mThreadHandler = Handler(Looper.myLooper()) {
          // 处理数据
        
          true
        }
        Looper.loop()
    }.start()
    

    如果我们不使用Looper.prepare(),继而使用Looper.loop()则程序会报错

    // Looper.java
    
    public static void loop() {
      final Looper me = myLooper();
      if (me == null) {
         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
      }
      ...
    }
    

    那么问题来了,我们的Handler是在主线程实现的,既然能一直接收消息,证明主线程是有Looper存在,并且执行了Looper.prepare()Looper.loop(),不然程序就会奔溃,那么这玩意到底是怎么在哪里创建出来的呢?

    答案是在ActivityThread类的main函数创建出来的

    // ActivityThread.java
    
    public static void main(String[] args) {
      ...     
      Looper.prepareMainLooper(); // 1
      ...
      ActivityThread thread = new ActivityThread();
      thread.attach(false, startSeq);
      if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
      }
      if (false) {
        Looper.myLooper().setMessageLogging(new
        LogPrinter(Log.DEBUG, "ActivityThread"));
      }
      // End of event ActivityThreadMain.
      Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
      Looper.loop(); // 2
      throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

    可以看到这里的12分别调用了我们想要看到的Looper.prepare()Looper.loop(),至于这个ActivityThread是什么,我们现在只需要知道它就是一个手机中每个应用的入口,正如我们运行Java程序时需要调用main()入口函数一样,这个类中也提供了一个main()函数,就是应用的入口函数,在这个入口函数中,就创建了我们需要的主线程Looper对象,所以我们直接在主线程实例化Handler对象就是使用的这个Looper对象,并可以一直接收消息

    既然Looper循环有了,Handler就能一直接收消息了。

    接下来我们从发送数据开始看起

    val obtain = Message.obtain()
    obtain.what = 1
    handler.sendMessage(obtain)
    

    以上就是创建一个Message然后发送出去,我们可能也常用handler.post(Runnable r),其实查看源码可以得知不管是用哪一种方式去发送数据,最后调用到Handler对象的一个方法

    // Handler.java
    
    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);
     }
    
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; // 1
        if (mAsynchronous) {
          msg.setAsynchronous(true);
         }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    可以看到1这里需要发送的msg的target指定了这个Handler(这里要记住),然后再用这个queue发送一个数据出去
    那么这里又疑惑了,这个queue又是哪里来的,也就是HandlermQueue对象从哪里来,其实上面的代码就已经提示了,在Handler的初始化构造函数里面就有

    // Handler.java
    
    mQueue = mLooper.mQueue;
    

    这时候我们可以理一下就是,Looper一直循环,Looper里面有一个消息队列MessageQueue,发送数据都是通过这个Looper里面的一个对象mQueue发送的

    接下来我们看这个消息队列是怎么发数据的,通过queue.enqueueMessage(msg, uptimeMillis);我们去看下MessageQueue类里面的enqueueMessage方法

    // MessageQueue.java
    
    boolean enqueueMessage(Message msg, long when) {
      ...
      synchronized (this) {
        if (mQuitting) {
          IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");
          Log.w(TAG, e.getMessage(), e);
          msg.recycle();
          return false;
        }
        msg.markInUse();
        msg.when = when;
    
        // 1,先获取当前准备要发送的消息
        Message p = mMessages; 
        boolean needWake;
        // 2,如果当前要准备的消息是空的,或者我们设置的时间是0
        //   意思就是此时消息队列里面是空的,或者我们设置的时间小于这个当前准备发送的消息
        //   则交换下顺序,把发送的消息和当前准备发送的消息调换一下顺序,即先发我们要发送的消息
        if (p == null || when == 0 || when < p.when) { // 2
          // 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();
    
          // 3,如果不能插入到当前准备发送的消息前面,则往后面依次进行排序,根据这个when时间
          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;
        }
    
        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
           nativeWake(mPtr);
         }
       }
      return true;
    }
    

    代码中标明了注释,仔细品品就知道这个queue.enqueueMessage(msg, uptimeMillis);,与其说是发送数据,更确切的应该说是对消息根据时间进行排序

    那么我们真正的发送消息是在哪里呢?

    我们知道Looper是一个可以带有循环机制的东西,Looper.loop即开始进行循环,我们可以看下这里面的代码,这个方法里面有很长代码,大家要挑重点看

    // Looper.java
     
    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;
            ...
            // 1,此时一直循环从队列中获取消息
            for (;;) {
                
                // 2,获取消息
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                ...
                try {
                    // 3,开始分发消息,此时的target即Handler
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                ...
            }
        }
    

    我们看代码注释2这个地方,调用queue.next,取出一个消息,然后注释3这块就是使用调用消息里面的target对象,由前面的代码分析可以知道这个target就是发送消息的handler,此时就把消息分发出去了,接下来我们可以查看下这个MessageQueuenext方法,和HandlerdispatchMessage方法

    MessageQueuenext方法

    // MessageQueue.java
    
    Message next() {
      ...
      for (;;) {
        if (nextPollTimeoutMillis != 0) {
          Binder.flushPendingCommands();
        }
        // 1,nativePollOnce 方法用于“等待”, 直到下一条消息可用为止,当没有消息的时候会阻塞住
        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;
    
          // 一般的,我们的msg.target是不会为空的,所以可以略过这条判断
          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());
           }
           // 2,开始取消息
           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);
             } 
             // 情况二:到了发送的时间了,则把下一个需要发送的消息赋值给mMessage
             //   并返回这个时候需要发送的消息
             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;
           }
           ...
         }
       }
    }
    

    这个方法注释写了很清楚,总的来说就是没有消息则阻塞等待,有消息的就取出消息

    HandlerdispatchMessage方法

    // Handler.java
    
    public void dispatchMessage(Message msg) {
      // 一般我们还可以使用Handler.post(Runnable r)方法发送消息
      // 这个时候的msg.callback 就是这个 r
      if (msg.callback != null) {
        handleCallback(msg);
      } else {
        if (mCallback != null) {
          if (mCallback.handleMessage(msg)) {
            return;
          }
        }
        handleMessage(msg);
      }
    }
    

    这个方法就简单了,如果我们设置了回调,则会回调出去,具体执行什么方法要看你怎么发送这个消息。

    上面我们举的例子是这样发的

    val obtain = Message.obtain()
    obtain.what = 1
    handler.sendMessage(obtain)
    

    如果使用上面的这个handler.sendMessage(Message msg)则会传消息给我们初始化的时候的回调函数

    val handler: Handler = Handler(
      Handler.Callback {
        when (it.what) {
          1 -> {
            // doSomething
          }
          else -> {}
        }
        true         
      }
    )
    

    还有另外一种发送消息的方法,就是使用post(Runnable r)发送

    handler?.post {
      object : Runnable {
        override fun run() {
    
        }
      }
    }
    

    我们可以看下这个post方法

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

    可以看到这里有个赋值,m.callback = r,所以我们post发送数据后对应的就是运行这个r

    public void dispatchMessage(Message msg) {
      // 一般我们还可以使用Handler.post(Runnable r)方法发送消息
      // 这个时候的msg.callback 就是这个 r
      if (msg.callback != null) {
        handleCallback(msg);
      } else {
        ...
      }
    }
    
    private static void handleCallback(Message message) {
      message.callback.run();
    }
    

    直接运行rrun方法

    解决了上面两种数据接收方式,还剩下一个handleMessage(msg)

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

    这个用于我们创建一个类,继承自Handler

    class Myhandler : Handler() {
      override fun handleMessage(msg: Message?) {
      }
    }
    

    这个时候使用如下方式发送消息,则会在上面重写的方法handleMessage中接收到消息

    val obtain = Message.obtain()
    obtain.what = 1
    myHandler.sendMessage(obtain)
    

    分析到这,基本算是把Handler机制给理清了,看下来可能有点吃力,不过如果大家自己跟着源码一步一步走下去,自己也是可以理解清楚的

    总结

    Handler机制:

    1,Looper消息循环机制 创建

    使用Looper.prepare(),创建一个Looper实例,并保存在Looper的一个静态变量sThreadLocal中,然后用Looper.loop()开启消息循环,此时创建的Handler可以传指定的Looper实例进去,如果是主线程的创建的Handler,则已经有Looper实例了,此时就不需要创建Looper实例

    2,Handler发送消息

    使用Handler.sendMessage(Message msg),或者Handler.post(Runnable r)发送消息,此时会调用HandlerenqueueMessage(),里面调用了MessageQueueenqueueMessage,此时就把消息根据发送时间进行了排序

    3,Looper.loop() 循环,检测消息并发送

    真实的发送放在了Looper.loop()方法里面,此时会调用Message.next()方法,获取一个消息,如果没有消息,next()方法则会阻塞等待,释放CPU,一旦有消息,则会唤醒,并在Looper.loop()方法里面进行消息的发送,然后消息发送又调用了Handler.dispatchMessage()方法,此时我们就可以接收到消息了

    相关文章

      网友评论

          本文标题:Android 必备知识之Handler机制

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