美文网首页Android开发教程
面试被问Handler有点懵? 一篇文章了解它

面试被问Handler有点懵? 一篇文章了解它

作者: 蜗牛是不是牛 | 来源:发表于2022-02-22 16:44 被阅读0次

    Handler有点懵? 一篇文章了解它

    使用

     val handler = Handler(Looper.getMainLooper(), object :Handler.Callback{
         override fun handleMessage(msg: Message): Boolean {
             return true
         }
     })
     
     //不论是post还是send最后都会在handleMessage中进行回调
     handler.post(object :Runnable {
         override fun run() {
     
         }
     })
     
     handler.sendEmptyMessage(1)
     
    
    

    源码分析

    Handler

    我们从handler的post方法开始

     public final boolean post(@NonNull Runnable r) {
        return  sendMessageDelayed(getPostMessage(r), 0);
     }
     
     //这里还是先把runnable 封装成一个message
     private static Message getPostMessage(Runnable r) {
         Message m = Message.obtain();
         m.callback = r;
         return m;
     }
     
     
     public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
         if (delayMillis < 0) {
             delayMillis = 0;
         }
         
         //1
         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
     }
     
    
    
    1. 执行时间 等于 现在的时间+延时的时间 ,来执行 。
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
     //这里其实就是取出mQueue 对其进行检查判断
     //1
     MessageQueue queue = mQueue;
     if (queue == null) {
         RuntimeException e = new RuntimeException(
                 this + " sendMessageAtTime() called with no mQueue");
         Log.w("Looper", e.getMessage(), e);
         return false;
     }
     //2
     return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    
    1. 在这里先看看mQueue是什么 ? 它是什么时候创建的 ?
    // MessageQueue类型的参数
        final MessageQueue mQueue;
        
        public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
            mLooper = looper;
            //Looper中取出
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    

    Looper.java

     //构造函数中创建   quitAllowed
     private Looper(boolean quitAllowed) {
         mQueue = new MessageQueue(quitAllowed);
         mThread = Thread.currentThread();
     }
    
    

    这里我们再看Looper的创建时机 。

    我们知道我们创建handler用的是 Looper.getMainLooper()获取的 。

     public static Looper getMainLooper() {
         synchronized (Looper.class) {
             return sMainLooper;
         }
     }
     
     @Deprecated
     public static void prepareMainLooper() {
         prepare(false);
         synchronized (Looper.class) {
             if (sMainLooper != null) {
                 throw new IllegalStateException("The main Looper has already been prepared.");
             }
             //这里进行的获取
             sMainLooper = myLooper();
         }
     }
     
     //这里获取到了 在一个sThreadLocal中获取
     public static @Nullable Looper myLooper() {
         return sThreadLocal.get();
     }
     
    
    

    这里还有几个问题 prepareMainLooper什么时候调用的 ? sThreadLocal是什么 ? 什么时候存储的Looper值 ?

    上面的两个问题先放下来 ,下面继续分析mQueue消息 。

    1. enqueueMessage(queue, msg, uptimeMillis);

       private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
               long uptimeMillis) {
           //将当前的Handler赋值给msg ,对应之后的回调
           msg.target = this;
           msg.workSourceUid = ThreadLocalWorkSource.getUid();
       
           if (mAsynchronous) {
               msg.setAsynchronous(true);
           }
           return queue.enqueueMessage(msg, uptimeMillis);
       }
      
      

      后面就进入了queue的内部

    MessageQueue

     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 { // 否则 按照时间顺序挂入到链表中
                 // 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;
             }
     
             // We can assume mPtr != 0 because mQuitting is false.
             if (needWake) {
                 nativeWake(mPtr);
             }
         }
         return true;
     }
    
    

    Looper

    上面讲了一个Runnable是如何封装成一个Message 然后进入到MessageQueue中 。

    下面我们分析是怎么取出来的 ?

    这里先分析一下 Looper的创建 。

    ActivityThread.java

     public static void main(String[] args) {
         
         //1这里创建了MainLooper
         Looper.prepareMainLooper();
     
         //2这里获取了主Handler 也是就mH
         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);
         //3执行
         Looper.loop();
     
         throw new RuntimeException("Main thread loop unexpectedly exited");
     }
    
    

    我们这里一个一个看

    第一个prepareMainLooper ,就是我们上面Looper的创建的地方 。 之后的所有创建就都连上了。

     public static void prepareMainLooper() {
         //调用prepare
         prepare(false);
         synchronized (Looper.class) {
             if (sMainLooper != null) {
                 throw new IllegalStateException("The main Looper has already been prepared.");
             }
             sMainLooper = myLooper();
         }
     }
    
    

    这个prepare干啥了呢 ?

     private static void prepare(boolean quitAllowed) {
         //这里可以看到prepare一个线程只能执行一次 也就是只有一个Looper
         if (sThreadLocal.get() != null) {
             throw new RuntimeException("Only one Looper may be created per thread");
         }
         //创建了一个当前的Looper放入了sThreadLocal中
         sThreadLocal.set(new Looper(quitAllowed));
     }
    
    

    这里暂且先不论sThreadLocal是个什么东西 ? 先姑且当做一个拥有 set get方法的类 。

    第二个sMainThreadHandler = thread.getHandler();

     final Handler getHandler() {
         return mH;
     }
     
     final H mH = new H();
     
     //其实就是一个自定义Handler
     class H extends Handler {
     }
    
    

    第三个Looper.loop();

     public static void loop() {
         final Looper me = myLooper();
         
         ...
         //获取Looper中的messageQueue
         final MessageQueue queue = me.mQueue;
     
         for (;;) {
             Message msg = queue.next(); // might block
             
             ...
     
             try {
                 //这里就是之前提到的 Handler 分发消息 
                 msg.target.dispatchMessage(msg);
             } catch (Exception exception) {
                 ...
             } finally {
                 ...
             }
     
             ...
             
             //处理完毕回收消息
             msg.recycleUnchecked();
         }
     }
    
    

    Handler.java

     public void dispatchMessage(@NonNull Message msg) {
         // post的方式
         if (msg.callback != null) {
             //执行runnable中的run 方法
             handleCallback(msg);
         } else { //send的方式
             if (mCallback != null) {
                 //处理消息 sendMessage中的消息 ,最后会回调自己复写的 handleMessage
                 if (mCallback.handleMessage(msg)) {
                     return;
                 }
             }
             
             handleMessage(msg);
         }
     }
    
    

    到这里整个Handler从消息的发送到回调就结束了 。

    ThreadLocal

    最后我们看一看这个神秘的 ThreadLocal

    set开始

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

    ThreadLocal.java

     public void set(T value) {
         //获取到当前线程
         Thread t = Thread.currentThread();
         //获取当前线程的map
         ThreadLocalMap map = getMap(t);
         if (map != null)
             //将Looper设置到map中 this是ThreadLocal
             map.set(this, value);
         else
             //如果为空进行创建
             createMap(t, value);
     }
     
     ThreadLocalMap getMap(Thread t) {
         //这里可以看出 Thread中存在ThreadLocal的ThreadLocalMap
         return t.threadLocals;
     }
     
     void createMap(Thread t, T firstValue) {
         //这里进行赋值
         t.threadLocals = new ThreadLocalMap(this, firstValue);
     }
    
    

    然后看他的 get

     public T get() {
         Thread t = Thread.currentThread();
         ThreadLocalMap map = getMap(t);
         if (map != null) {
             //根据当前ThreadLocal的键的hash值获取entry
             ThreadLocalMap.Entry e = map.getEntry(this);
             if (e != null) {
                 @SuppressWarnings("unchecked")
                 //取值 也就是我们的Looper
                 T result = (T)e.value;
                 //返回
                 return result;
             }
         }
         return setInitialValue();
     }
    
    

    由此可知,每一个线程对应一个ThreadLocal,内部存储了唯一的Looper。

    又说明了一个问题 ,如果我们自己的Thread中 ,想要使用Handler 需要调用Looper.prepare() Looper.loop() 了。

    扩展

    同步屏障

    Message分为: 同步消息 异步消息 屏障消息

    • 普通消息都为同步消息
    • 异步消息 可以通过Handler async参数传递true,或者设置Message的属性 setAsynchronous = true 让消息编程异步消息
    • 屏障消息 由 MessageQueue的postSyncBarrier方法发送 ,该方法为私有方法 ,需要使用反射才能进行调用

    屏障消息 因为会将同步消息过滤掉 ,所以也叫做同步屏障

     private int postSyncBarrier(long when) {
         // Enqueue a new sync barrier token.
         // We don't need to wake the queue because the purpose of a barrier is to stall it.
         synchronized (this) {
             final int token = mNextBarrierToken++;
             //插入一条屏障消息
             final Message msg = Message.obtain();
             msg.markInUse();
             //when是当前时间 也就意味着当前时间之后的同步消息都不会执行了
             msg.when = when;
             msg.arg1 = token;
             //屏障消息没有设置target
             Message prev = null;
             Message p = mMessages;
             if (when != 0) {
                 //按照时间将屏障消息插入到适当的位置
                 while (p != null && p.when <= when) {
                     prev = p;
                     p = p.next;
                 }
             }
             
             //如果首个不为空 则插入之后 为空则放入首位 
             //屏障后面的所有消息
             if (prev != null) { // invariant: p == prev.next
                 msg.next = p;
                 prev.next = msg;
             } else {
                 msg.next = p;
                 mMessages = msg;
             }
             return token;
         }
     }
    
    

    我们看看如何屏障的 ?

    消息是从MessageQueue的next的方法中依次取出,我们看next方法

     Message next() {
         int pendingIdleHandlerCount = -1; // -1 only during first iteration
         int nextPollTimeoutMillis = 0;
         for (;;) {
             if (nextPollTimeoutMillis != 0) {
                 Binder.flushPendingCommands();
             }
     
             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 == null 则为屏障消息
                 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());
                     
                 }
                 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 {
                             mMessages = msg.next;
                         }
                         msg.next = null;
                         if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                         msg.markInUse();
                         //返回该异步消息
                         return msg;
                     }
                 } else {
                     // No more messages.
                     nextPollTimeoutMillis = -1;
                 }
             ...
             
         }
     }
    
    

    看到这里大家应该也就明白了 ,通过设置同步屏障 ,优先取出所有的异步消息执行。

    这里自然而然就想到了View的绘制,因为它需要很高的优先级,系统会默认调用屏障消息来保证优先处理我们的布局 。

    总结

    Handler在Android中占据举足轻重的位置 ,因为Android设计的一个特殊性 ,所有的UI刷新操作都在主线程(当然这样做也是为了让界面刷新更高效),这就势必离不开线程的切换 ,而Handler的目的正在此 。

    文章中如果有问题的地方,欢迎大家多多评论指正,另外还是希望得到大家的点赞支持

    相关推荐

    相关文章

      网友评论

        本文标题:面试被问Handler有点懵? 一篇文章了解它

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