软件编程
位置:首页>> 软件编程>> Android编程>> Android Handler源码深入探究

Android Handler源码深入探究

作者:niuyongzhi  发布时间:2023-02-17 11:54:53 

标签:Android,Handler,源码

1.android 消息循环有4个重要的类Handler、Message、Looper、MessageQueue

handler 用来发送、处理消息。

Message 是消息的载体。

MessageQueue 是一个消息队列,既然是队列,就有入队、出队的处理。

Looper 创建一个消息循环。不断的从MessageQueue中读取消息、并分发给相应的Handler进行处理。

2.我们都知道main函数是Java程序的入口,android程序也不例外。

android App的唯一入口就是ActivityThread中的 main函数。 这个函数是由Zygote创建app进程后 通过反射的方式调用的。

当一个App启动时,会先执行这个main方法,在ActivityThread,main方法中,

public static void main(String[] args) {
         //创建一个消息循环
         Looper.prepareMainLooper();
         //创建ActivityThread对象
         ActivityThread thread = new ActivityThread();
         //创建Application、启动MainActivity
         thread.attach(false, startSeq);
         //使消息循环奔跑起来
         Looper.loop();
         //抛了一个异常 主线程的Looper 意外退出了,
         //所以loop中的for循环要阻塞在这里,一旦main函数执行完毕,进程也就退出了。
         //并且一直要提取消息,处理消息。
         throw new RuntimeException("Main thread loop unexpectedly exited");
     }

3. 首先看Looper是如何创建的。

Looper.prepareMainLooper();
   public static void prepareMainLooper() {
       prepare(false);
       //同步方法保证一个sMainLooper 只被赋值一次。
       synchronized (Looper.class) {
           if (sMainLooper != null) {
               throw new IllegalStateException("The main Looper has already been prepared.");
           }
           sMainLooper = myLooper();
       }
   }
   //创建一个Looper对象,并保存在了 sThreadLocal中。关于ThreadLocal,也是很重要的一个知识点。
    private static void prepare(boolean quitAllowed) {
       //一个线程只能有一个Looper,
       if (sThreadLocal.get() != null) {
           throw new RuntimeException("Only one Looper may be created per thread");
       }
       sThreadLocal.set(new Looper(quitAllowed));
    }
     public static @Nullable Looper myLooper() {
           return sThreadLocal.get();
     }
  //在Looper中创建了MessageQueue
  private Looper(boolean quitAllowed) {
       mQueue = new MessageQueue(quitAllowed);
       mThread = Thread.currentThread();
  }

至此,创建的代码执行完毕。

总结一句话就是,app启动时,会创建Looper,并且保证一个线程只能创建一个Looper。

创建Looper的同时,也创建的消息队列 MessageQueue。这些都是消息循环的准备工作。

通过Looper.loop,这个消息循环就跑起来了。

Looper.loop();
   /**
   * Run the message queue in this thread.
   */
    public static void loop() {
       for (; ; ) {
           //从消息循环中提取消息。消息时会阻塞在这里。
           Message msg = queue.next(); // might block
           //target-->Handler.Msg持有handler的引用。
           msg.target.dispatchMessage(msg);
       }
   }

4.在MessageQueue中enqueueMessage()插入消息、next()提取消息方法。

插入消息
   //Message.obtain()从Message消息缓存池内获得一个消息。
   handler.sendMessage(Message.obtain());
   public final boolean sendMessage(Message msg){
       return sendMessageDelayed(msg, 0);
   }
   //我们发送的延迟消息,写入到消息循环中都是一个时间戳,当前时间+延迟时间,未来某个时间。
  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;
          return enqueueMessage(queue, msg, uptimeMillis);
   }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
           //this==handler,在这里给message.target赋值了handler。
           msg.target = this;
           if (mAsynchronous) {
               msg.setAsynchronous(true);
           }
           return queue.enqueueMessage(msg, uptimeMillis);
    }
//在MessageQueue中用一个单线链表来保存消息。
    //在这个消息的单链表中,是按消息执行的时间先后,从小到大排序的。
    //mMessages 是这个单链表的第一个消息。
    boolean enqueueMessage(Message msg, long when) {
         //通过sendMessage并不能发送handler==null的消息。
        if (msg.target == null) {
             throw new IllegalArgumentException("Message must have a target.");
         }
        synchronized (this) {
            msg.markInUse();
            msg.when = when;
            //将链表中第一个消息赋值给p
            Message p = mMessages;
            boolean needWake;
            //如果p==null说明消息列表中没有要被执行的消息。
            //如果when==0说明新新添加的消息要被马上执行,所以要排在列表的头部
            //如果when<p.when,说明新添加的消息,比消息队列第一个消息要先执行,所以也要放在头部
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                //将原来的头部消息赋值给新消息的next
                msg.next = p;
                //将新加的消息赋值给mMessage。因为mMessages这个变量用来保存消息列表的第一个消息。
                mMessages = msg;
            } else {//如果when>=p.when,则需要遍历消息队列,将新添加的消息插入到队列中间,
                Message prev;
                for (;;) {
                    prev = p;//把当前的赋值给前一个
                    p = p.next;//把下一个赋值给当前的
                    //如果p==null说明已经遍历到了链表末尾。
                    //如果新增的消息时间小于了p的when。那么这个消息应该插入到prev之后,p之前。
                    if (p == null || when < p.when) {
                        break;
                    }
                }
                //新增消息在p之前,prev之后
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        return true;
  }

提取消息:

Message next() {
          int nextPollTimeoutMillis = 0;
          for (;;) {
              if (nextPollTimeoutMillis != 0) {
                  Binder.flushPendingCommands();
              }
              //开始休眠,nextPollTimeoutMillis下次被唤醒的时间
              //如果是-1则一直休眠,直到有新的消息再唤醒消息队列。
              nativePollOnce(ptr, nextPollTimeoutMillis);
              synchronized (this) {
                  // Try to retrieve the next message.  Return if found.
                  //开始遍历消息队列,返回找到的消息。
                  final long now = SystemClock.uptimeMillis();
                  Message prevMsg = null;
                  //消息队列,头部消息
                 //如果msg!=null,但是msg.target==null,sendMessage中,是不允许发送handler为null的消息的。
                  //target==null的消息是系统发送的,先发送一个同步屏障消息,再发送直到isAsynchronous = true的异步消息。
  //这样做的目的就保证了这个异步消息有更高优先级被执行,先从消息队列中提取。
                  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());//直到isAsynchronous = true,也就是找到了同步屏障的异步消息
                  }
                  }
                  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 {
                              //如果前一个消息是null,说明当前就是消息头,
                              //将消息队列头部消息指向当前提取出消息的下一个消息。
                              mMessages = msg.next;
                          }
                          //将找到的消息的下一个赋值null,和原来的消息队列脱离关系。
                          msg.next = null;
                          if (false) Log.v("MessageQueue", "Returning message: " + msg);
                          return msg;
                      }
                  } else {//如果msg==null
                      // No more messages.
                      nextPollTimeoutMillis = -1;
                  }
              }
          }
      }

handler会涉及到native的代码。在native层使用的epoll机制,这个后面在深入分享。

这里涉及到了一个消息屏障的概念,有机会单独写文章来分享。

//同步屏障消息
   void scheduleTraversals() {
       if (!mTraversalScheduled) {
           mTraversalScheduled = true;
           //发送一个同步屏障消息
           mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
           mChoreographer.postCallback(
                   Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
       }
   }

5. 至此,整个消息循环大体的流程已经完成。但是关于handler的面试题很多。

比如为啥handler会导致Activity内存泄漏?如何解决?

内存泄漏的本质就是长声明周期对象持有短声明周期对象的引用,导致短声明周期对象,不再使用但内存却无法被回收。

我们知道handler作为Activity的内部类,持有外部类的引用,所以整个引用链是

Activity-->handler-->Message-->MessageQueue.

当activity退出后,如果消息为来的及处理,就有可能会导致Activity无法被GC回收,从而导致内存泄漏。

handler.post(),发送的消息执行在子线程还是主线程?

下面来看消息池。消息池也是一个单项链表,长度是50.

静态对象sPool就是消息队列的头部Message。

每次获取消息时,都会返回消息池中第一个对象。

Message.obtain()
    private static Message sPool;
    private static final int MAX_POOL_SIZE = 50;
    public static Message obtain() {
           synchronized (sPoolSync) {
               if (sPool != null) {
                   Message m = sPool;
                   sPool = m.next;
                   m.next = null;
                   m.flags = 0; // clear in-use flag
                   sPoolSize--;
                   return m;
               }
           }
           return new Message();
     }

来源:https://blog.csdn.net/niuyongzhi/article/details/125882150

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com