快速理解Handler机制

前期准备 1 、2、 3 步骤

使用 4、5、6步骤

1. ActivityThread main方法(ActivityThread类中)

 Looper.prepareMainLooper();// 为UI(祝线程)创建1个循环器对象
 Looper.prepare() // 为当前线程(子线程)创建1个循环器对象
 Looper.loop();// 开启轮循

2. prepareMainLooper() ( Looper类中)

  1. UI线程由prepareMainLooper函数内部调用自动生成Looper
  2. 子线程手动调用Looper.prepare()生成Looper对象
 public static final void prepare() {
    // sThreadLocal用于存储线程的变量
        sThreadLocal.set(new Looper(true));
    }
2.1 Looper构造函数
  private Looper(boolean quitAllowed) {

            mQueue = new MessageQueue(quitAllowed);
            // 1. 创建1个消息队列对象(MessageQueue)
            // 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)
            mThread = Thread.currentThread();
        }

3. Looper.loop();( Looper类中)

开启消息轮循

/** 
  * 源码分析: Looper.loop()
  * 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
  * 特别注意:
  *       a. 主线程的消息循环不允许退出,即无限循环
  *       b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
  */
  public static void loop() {//只贴核心代码
       //1. 获取当前Looper的消息队列
            final Looper me = myLooper();
            final MessageQueue queue = me.mQueue;
                for (;;) {
       //2. next()取出消息
            Message msg = queue.next(); // might block
       //3. 分发消息:调用msg内部的Handler的dispatchMessage
            msg.target.dispatchMessage(msg);
            // 释放资源
            msg.recycleUnchecked();
        }
  }
        
3.1 next()取出消息( MessageQueue类中)

next()是MessageQueue中方法

Message next() {
     
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // native层调用,没有消息会在这里阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

           
        }
    }

3.2 dispatchMessage分发消息 ( Handler 类中)

dispatchMessage是Handler中的方法

  public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 使用者处理消息
            handleMessage(msg);
        }
    }

使用

4 .new Handler()

4.1 Handler构造方法做了什么事情?
  1. 获取当前线程Looper
  2. 关联MessageQueue
 public Handler(Callback callback, boolean async) {
           // 仅贴出关键代码
            // 1. 指定Looper对象
                mLooper = Looper.myLooper();
            // 2. 绑定消息队列对象(MessageQueue)
                mQueue = mLooper.mQueue;
                // 获取该Looper对象中保存的消息队列对象(MessageQueue)
                // 至此,保证了handler对象 关联上 Looper对象中MessageQueue
    }

5. 创建消息对象Message

Message msg = Message.obtain()

  • Message内部维护了1个Message池,用于Message消息对象的复用
  • 使用obtain()则是直接从池内获取:避免每次都使用new重新分配内存
  • 若池内无消息对象可复用,则还是用关键字new创建

/** 
  * 源码分析:Message.obtain()
  * 作用:创建消息对象
  * 注:创建Message对象可用关键字new 或 Message.obtain()
  */
  public static Message obtain() {

        // Message内部维护了1个Message池,用于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;
            }
            // 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存
        }
        // 若池内无消息对象可复用,则还是用关键字new创建
        return new Message();

    }

6. 在工作线程中 发送消息到消息队列中

最后会都会调用enqueueMessage方法(Handler类中)


            private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
                 // 1. 将msg.target赋值为this
                 // 即 :把 当前的Handler实例对象作为msg的target属性
                 msg.target = this;
                 // 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
                 // 实际上则是将该消息派发给对应的Handler实例        

                // 2. 调用消息队列的enqueueMessage()
                // 即:Handler发送的消息,最终是保存到消息队列->>分析4
                return queue.enqueueMessage(msg, uptimeMillis);
        }
6.1 enqueueMessage

属于消息队列类(MessageQueue类)的方法

        /** 
          * 定义:属于消息队列类(MessageQueue)的方法
          * 作用:入队,即 将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
          * 采用单链表实现:提高插入消息、删除消息的效率
          */
          boolean enqueueMessage(Message msg, long when) {

                synchronized (this) {

                    msg.markInUse();
                    msg.when = when;
                    Message p = mMessages;
                    boolean needWake;

                    // 判断消息队列里有无消息
                        // a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
                        if (p == null || when == 0 || when < p.when) {
                            msg.next = p;
                            mMessages = msg;
                            needWake = mBlocked;
                        } else {
                            needWake = mBlocked && p.target == null && msg.isAsynchronous();
                            Message prev;

                        // b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
                            for (;;) {
                                prev = p;
                                p = p.next;
                                if (p == null || when < p.when) {
                                    break;
                                }
                                if (needWake && p.isAsynchronous()) {
                                    needWake = false;
                                }
                            }

                            msg.next = p; 
                            prev.next = msg;
                        }

                        if (needWake) {
                            nativeWake(mPtr);
                        }
                    }
                    return true;
            }

send…和 post…区别

  1. 消息对象的创建 = 内部 根据Runnable对象而封装
  2. 发送到消息队列的逻辑 =sendM(Message msg)

常问问题

  1. Looper 死循环为什么不会导致应用卡死?
    会阻塞在MessageQueue的next方法nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作

Handler 同步屏障

  1. 如何设置同步屏障?

下面 postSyncBarrier 方法创建一个MSG对象,但是MSG的target属性没有赋值

MessageQueue类:

    private int postSyncBarrier(long when) {
       
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
        // .....
            return token;
        }
    }

Handler.postXXXX/sendXXXX,最后调用enqueueMessage时,都会为MSG的target赋值

Handler类:


    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
            // 为target赋值
        msg.target = this;
       //....
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看出设置同步屏障,创建的msg,内部的target是空值。Handler发送消息最终都会在enqueueMessage中进行target赋值

MessageQueue.next()

同步屏障会在取消息中体现出来。

如果设置了同步屏障,Handler会优先处理异步消息

 Message next() {
       //....
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            // 没有消息,进入休眠状态
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
              
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 同步屏障,过滤掉同步消息,找到最近的异步消息
                    // 异步消息优先级高于同步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
              
               //....
        }
    }

应用场景:

Activity启动后,在绘制前中有使用到同步屏障。

ViewRootImpl类:

 void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 使用同步屏障,确保mTraversalRunnable先执行
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
         //....
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值