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

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的目的正在此 。

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

相关推荐

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容