美文网首页
Handler原理剖析

Handler原理剖析

作者: pphdsny | 来源:发表于2018-01-11 21:09 被阅读8次

    API说明

    构造函数

    public Handler(Looper looper, Callback callback, boolean async) {
      mLooper = looper;
      mQueue = looper.mQueue;
      mCallback = callback;
      mAsynchronous = async;
    }
    

    Handler提供的构造方法有很多,归根到底是初始化一些核心参数:

    • Looper

      在一个线程中运行一个消息队列的Loop

    • MessageQueue

      带分发的消息队列

    • Message

      发送的消息

    Looper

    获取Looper对象

    主线程的Looper会在应用启动时通过ActivityThread,如果在子线程new Handler会报错,因为没有创建该线程下的Looper。

    public static @Nullable Looper myLooper() {
      return sThreadLocal.get();
    }
    //从静态参数中获取
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    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));
    }
    public static void prepareMainLooper() {
      prepare(false);
      synchronized (Looper.class) {
        //只允许被调用一次
        if (sMainLooper != null) {
          throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
      }
    }
    //prepareMainLooper调用,通过ActivityThread的main方法中调用
    public static void main(String[] args) {
      //初始化Looper
      Looper.prepareMainLooper();
    
      ActivityThread thread = new ActivityThread();
      thread.attach(false);
    
      if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
      }
    
      if (false) {
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
      }
    
      //开启looper循环
      Looper.loop();
    }
    
    

    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;
      for (;;) {//死循环
        //当queen返回null时候,loop会中断执行,相当于quit
        Message msg = queue.next(); // might block
        if (msg == null) {
          // No message indicates that the message queue is quitting.
          return;
        }
        try {
          //分发消息
          //msg.target就是发送消息的Handler
          msg.target.dispatchMessage(msg);
        } finally {
        }
        //回收消息
        msg.recycleUnchecked();
      }
    }
    

    Handler.dispathcMessage()

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

    MessageQueue

    MessageQueue的Native实现

    核心方法:next()

    
    Message next() {
            // Return here if the message loop has already quit and been disposed.
            // This can happen if the application tries to restart a looper after quit
            // which is not supported.
            // 如果消息循环已经退出了。则直接在这里return。因为调用disposed()方法后mPtr=0
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }
            //记录空闲时处理的IdlerHandler的数量
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            // native层用到的变量 ,如果消息尚未到达处理时间,则表示为距离该消息处理事件的总时长,
            // 表明Native Looper只需要block到消息需要处理的时间就行了。 所以nextPollTimeoutMillis>0表示还有消息待处理
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    //刷新下Binder命令,一般在阻塞前调用
                    Binder.flushPendingCommands();
                }
                // 调用native层进行消息标示,nextPollTimeoutMillis 为0立即返回,为-1则阻塞等待。
                nativePollOnce(ptr, nextPollTimeoutMillis);
                //加上同步锁
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    // 获取开机到现在的时间
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    // 获取MessageQueue的链表表头的第一个元素
                    Message msg = mMessages;
                     // 判断Message是否是障栅,如果是则执行循环,拦截所有同步消息,直到取到第一个异步消息为止
                    if (msg != null && msg.target == null) {
                         // 如果能进入这个if,则表面MessageQueue的第一个元素就是障栅(barrier)
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        // 循环遍历出第一个异步消息,这段代码可以看出障栅会拦截所有同步消息
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                           //如果msg==null或者msg是异步消息则退出循环,msg==null则意味着已经循环结束
                        } while (msg != null && !msg.isAsynchronous());
                    }
                     // 判断是否有可执行的Message
                    if (msg != null) {  
                        // 判断该Mesage是否到了被执行的时间。
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            // 当Message还没有到被执行时间的时候,记录下一次要执行的Message的时间点
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Message的被执行时间已到
                            // Got a message.
                            // 从队列中取出该Message,并重新构建原来队列的链接
                            // 刺客说明说有消息,所以不能阻塞
                            mBlocked = false;
                            // 如果还有上一个元素
                            if (prevMsg != null) {
                                //上一个元素的next(越过自己)直接指向下一个元素
                                prevMsg.next = msg.next;
                            } else {
                               //如果没有上一个元素,则说明是消息队列中的头元素,直接让第二个元素变成头元素
                                mMessages = msg.next;
                            }
                            // 因为要取出msg,所以msg的next不能指向链表的任何元素,所以next要置为null
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            // 标记该Message为正处于使用状态,然后返回Message
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                        // 没有任何可执行的Message,重置时间
                        nextPollTimeoutMillis = -1;
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    // 关闭消息队列,返回null,通知Looper停止循环
                    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.
                    // 当第一次循环的时候才会在空闲的时候去执行IdleHandler,从代码可以看出所谓的空闲状态
                    // 指的就是当队列中没有任何可执行的Message,这里的可执行有两要求,
                    // 即该Message不会被障栅拦截,且Message.when到达了执行时间点
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    
                    // 这里是消息队列阻塞( 死循环) 的重点,消息队列在阻塞的标示是消息队列中没有任何消息,
                    // 并且所有的 IdleHandler 都已经执行过一次了
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
        
                    // 初始化要被执行的IdleHandler,最少4个
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }
    
                // Run the idle handlers.
                // We only ever reach this code block during the first iteration.
                // 开始循环执行所有的IdleHandler,并且根据返回值判断是否保留IdleHandler
                for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                        Log.wtf(TAG, "IdleHandler threw exception", t);
                    }
    
                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
    
                // Reset the idle handler count to 0 so we do not run them again.
                // 重点代码,IdleHandler只会在消息队列阻塞之前执行一次,执行之后改标示设置为0,
                // 之后就不会再执行,一直到下一次调用MessageQueue.next() 方法。
                pendingIdleHandlerCount = 0;
    
                // While calling an idle handler, a new message could have been delivered
                // so go back and look again for a pending message without waiting.
                // 当执行了IdleHandler 的 处理之后,会消耗一段时间,这时候消息队列里的可能有消息已经到达 
                 // 可执行时间,所以重置该变量回去重新检查消息队列。
                nextPollTimeoutMillis = 0;
            }
        }
    

    Message

    内部实现单链表存储

    //message标示
    public int what;
    //保存相关对象
    public int arg1;
    public int arg2;
    public Object obj;
    int flags;      //当前状态
    long when;      //延时时间
    Bundle data;    //数据绑定
    Handler target; //发送目标Handler
    Runnable callback;  //Handler.post发送的Runnable回调
    // sometimes we store linked lists of these things
    Message next;   //链式存储
    //同步锁
    private static final Object sPoolSync = new Object();
    //Message复用池
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
    //回收时是否检查Message状态
    private static boolean gCheckRecycle = true;
    

    获取Message对象

    public static Message obtain() {
      synchronized (sPoolSync) {
        if (sPool != null) {
          //当前sPool对象所指的Message
          Message m = sPool;
          //将sPool链表后移一位,大小-1
          sPool = m.next;
          //将Message从链表中断裂
          m.next = null;
          m.flags = 0; // clear in-use flag
          sPoolSize--;
          return m;
        }
      }
      return new Message();
    }
    

    回收

    void recycleUnchecked() {
      // Mark the message as in use while it remains in the recycled object pool.
      // Clear out all other details.
      flags = FLAG_IN_USE;
      what = 0;
      arg1 = 0;
      arg2 = 0;
      obj = null;
      replyTo = null;
      sendingUid = -1;
      when = 0;
      target = null;
      callback = null;
      data = null;
    
      synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
          //将使用完的Message添加到链表头
          next = sPool;
          sPool = this;
          sPoolSize++;
        }
      }
    }
    

    Handler

    发送消息的方法:postXxx、sendXxx

    //postXxx会生成一个Message后再调用后续sendMessageAtTime方法
    private static Message getPostMessage(Runnable r) {
      Message m = Message.obtain();
      m.callback = r;
      return m;
    }
    //sendXxx最终都会调用到下面方法
    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);
        }
    //将消息队列添加到MessageQueue中
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
      msg.target = this;
      if (mAsynchronous) {
        msg.setAsynchronous(true);
      }
      return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    使用Handler的内存泄露

    使用Handler造成的内存泄露会有以下几种情况

    • 使用非静态申明Handler
    public class SampleActivity extends Activity {
      private final Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          // ... 
        }
      }
    }
    

    在使用handler时,这是一段很常见的代码。但是,它却会造成严重的内存泄漏问题。在实际编写中,我们往往会得到如下警告:

    Handler classes should be static or leaks might occur.
    

    分析:

    1、在java里,非静态内部类和匿名类都会潜在的引用它们所属的外部类,静态内部类却不会。

    2、Handler会中有个一直循环的Looper。当主线程里,实例化一个Handler对象后,它就会自动与主线程Looper的消息队列关联起来,所以其持有外部对象Activity就会导致无法被销毁回收。

    • 发送延时消息
    mLeakyHandler.postDelayed(new Runnable() {
      @Override
      public void run() { 
        //
      }
    }, 1000 * 60 * 10);
    finish();
    

    问题:

    1、匿名内部类默认持有外部对象

    2、内部类实例的生命周期比Activity更长

    Handler正确的使用

    //1、弱引用外部对象
    public abstract class WeakHandler<T> extends Handler {
    
        private final WeakReference<T> targetWeak;
    
        public WeakHandler(T autoScrollViewPager) {
            this.targetWeak = new WeakReference<T>(autoScrollViewPager);
        }
    
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            T target = this.targetWeak.get();
            if (target == null) {
                return;
            }
            handleWeakMessage(msg, target);
        }
    
        public abstract void handleWeakMessage(Message msg, T target);
    }
    //2、Activity中申明静态
    private static WeakHandler handler = new WeakHandler();
    //3、Activity onDestory中销毁所有消息
    handler.removeCallbacksAndMessages(null);
    

    相关文章

      网友评论

          本文标题:Handler原理剖析

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