美文网首页Android知识Android开发经验谈Android开发
【Android源码】Handler 机制源码分析

【Android源码】Handler 机制源码分析

作者: 指间沙似流年 | 来源:发表于2017-07-08 18:39 被阅读82次

    为什么要使用Handler

    1. 因为在Android中访问UI只能在主线程中进行,如果在子线程中运行,则程序会抛出异常。

      // ViewRootImpl.java
      void checkThread() {
         if (mThread != Thread.currentThread()) {
             throw new CalledFromWrongThreadException(
                     "Only the original thread that created a view hierarchy can touch its views.");
         }
      }
      
    2. 为什么不允许在子线程中访问UI?

      因为Android的UI并不是线程安全的,如果在多线程中执行UI的操作,那么UI的状态是不可控的,这个时候就会出现各种问题。
      那么最好的办法就是只能在一个线程中执行UI的操作。
      而Android主线程中又不能执行耗时操作,因为那样就会导致程序的ANR。
      所以Android提供了Handler这样的机制,用来在子线程中执行耗时操作之后,发送消息给主线程,主线程再执行UI的更新操作。

    Handler机制原理分析

    这里我们以Android UI线程的Handler来分析。

    Handler的创建

    Android的应用入口是ActivityThread.main()函数,UI线程的Handler就是在这个函数中创建的。

    public static void main(String[] args) {
        Looper.prepareMainLooper();
        thread.attach(false);
        ActivityThread thread = new ActivityThread();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        Looper.loop();
    }
    

    当main函数执行之后,应用就启动了,从这个时候开始,Looper就会一直从消息队列中取出消息并处理。而应用会通过Handler来不断的添加消息给消息队列。通过不断的添加消息和取出消息,整个消息机制就被运转起来了。

    Looper

    从上面可以看到在Handler创建前后,通过Looper的prepareloop方法,创建了Looper对象和开启消息循环。

    1. 构造函数

      private Looper(boolean quitAllowed) {
         mQueue = new MessageQueue(quitAllowed);
         mThread = Thread.currentThread();
      }
      
      1. 创建了MessageQueue对象,这个对象是消息队列,主要用来存放我们的消息,会在下面具体分析。
      2. 获取当前的线程并保存起来
    2. prepare

      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));
      }
      
      1. 判断ThreadLocal中在当前线程是否已经存在了Looper对象,如果存在,则抛出异常,所以可以得出在同一个线程中只能存在一个Looper对象。
      2. 在ThreadLocal存入当前线程的Looper对象。
      3. ThreadLocal对象是一个线程内部的数据存储类,通过它可以在指定的线程存储数据,当存储数据之后,就只能在指定的线程中获取到存储的数据,其他线程是不能获取到数据的。
    3. loop

      public static void loop() {
         final Looper me = myLooper();
         // 获取消息队列
         final MessageQueue queue = me.mQueue;
          // 死循环来轮询的从消息队列中获取消息
         for (;;) {
             Message msg = queue.next(); // might block
             if (msg == null) {
                 return;
             }
             
             try {
                 msg.target.dispatchMessage(msg);
             } finally {
                 if (traceTag != 0) {
                     Trace.traceEnd(traceTag);
                 }
             }
      
             msg.recycleUnchecked();
         }
      }
      
      1. loop函数会不断的从消息队列中取出消息,当queue.next()为空的时候就直接阻塞住
      2. msg不为空,调用msg.target.dispatchMessage(msg)处理消息,而msg.target其实就是Handler对象,取出消息之后就交给Handler来处理发送的消息了。
    4. quit

      public void quit() {
         mQueue.quit(false);
      }
      public void quitSafely() {
          mQueue.quit(true);
      }
      

      调用MessageQueue的quit方法来退出Looper。其中上面的是直接退出,下面的是安全的退出,也就是只标记一下,当消息队列中的消息全部执行完成之后安全的退出。

      当我们在子线程中创建Looper和Handler的时候,如果我们不调用quit方法的话,子线程就会一直处于等待状态,所以我们需要调用quit方法退出Looper。

    MessageQueue

    在Looper中大量的使用到了MessageQueue,而MessageQueue主要就是两个操作插入消息和读取消息并删除消息,我们来一起分析下:

    1. 构造函数

      MessageQueue(boolean quitAllowed) {
         mQuitAllowed = quitAllowed;
         mPtr = nativeInit();
      }
      

      在构造函数中,调用了nativeInit方法在Native层创建了NativeMessageQueue和Looper,并将Looper设置给当前的线程。这个时候Java层和Native层都有了MessageQueue和Looper。
      在Native层的Looper中,创建了一个管道,本质上就是一个文件,一个管道中有读和写两个文件操作符,通过读和写来将消息写入和读取。

    2. enqueueMessage 插入消息

      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;
             } else {
                 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;
             }
      
             // We can assume mPtr != 0 because mQuitting is false.
             if (needWake) {
                 nativeWake(mPtr);
             }
         }
         return true;
      }
      

      enqueueMessage其实就是构建好msg,并插入到单链表中。

    3. next 读取消息并删除消息

      Message next() {
          for (;;) {
              nativePollOnce(ptr, nextPollTimeoutMillis);
          }
      }
      

      next方法是一个无限循环方法,当消息队列中没有消息的时候,next方法会被阻塞在这里,当有新的消息的时候,next方法会最终返回这条消息并从单链表中移除。

    Handler

    1. 构造函数

      public Handler(Callback callback, boolean async) {
      
         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;
      }
      
      1. 首先获取Looper对象,如果Looper对象为空,则抛出异常,所以当我们在子线程中创建Handler的时候,首先要通过Looper.prepare()来创建子线程的Looper对象。
      2. 通过Looper获取到MessageQueue
    2. 发送消息

      Handler的发送消息主要是post的方法和send的方法,而他们最终调用的都是:

      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;
          if (mAsynchronous) {
              msg.setAsynchronous(true);
          }
          return queue.enqueueMessage(msg, uptimeMillis);
      }
      

      Handler主要就是向消息队列中插入了一条消息,这个时候MessageQueue就会调用插入的方法来插入消息。

    3. 读取消息

      当Handler发送消息之后,MessageQueue就会通过next方法读取出这个消息,那么Looper的loop就不会被阻塞住,取出消息之后就调用了msg.target.dispatchMessage(msg)方法,最终交给Handler的dispatchMessage来执行。

      /**
      * Handle system messages here.
      */
      public void dispatchMessage(Message msg) {
         if (msg.callback != null) {
             handleCallback(msg);
         } else {
             if (mCallback != null) {
                 if (mCallback.handleMessage(msg)) {
                     return;
                 }
             }
             handleMessage(msg);
         }
      }
      
      1. 检查Message的callback是否为空,不为空则通过handleCallback来处理,而callback就是通过post传递过来的Runnable对象。直接调用run方法执行。

        private static void handleCallback(Message message) {
            message.callback.run();
        }
        
      2. 检查mCallback是否为空,不为空则调用mCallback.handleMessage方法处理消息。而mCallback就是我们在创建Handler的时候传递过去的callback。

        public interface Callback {
            public boolean handleMessage(Message msg);
        }
        public Handler(Callback callback) {
            this(callback, false);
        }
        
      3. 调用handleMessage(msg)处理消息。

    相关文章

      网友评论

        本文标题:【Android源码】Handler 机制源码分析

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