Handler机制(一)

Handler基础

Handler机制是什么?

Handler是用来处理线程间通信的一套机制。

初级使用

第一步:在主线程中定义Handler

    private Handler mHandler = new Handler(Looper.myLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if(msg.what == 0){
                ((Button) findViewById(R.id.bt_test)).setText((String)msg.obj); //定义的一个button
            }
        }
    };

第二步:在子线程中生成Message并发送

        findViewById(R.id.bt_test).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(2000);//模拟子线程耗时任务
                            Message msg = new Message();
                            msg.what = 0;
                            msg.obj = "end";
                            mHandler.sendMessage(msg);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }).start();
            }
        });

上面做了以下几个步骤:1.在主线程中创建Handler对象并实现handleMessage方法用来处理收到的Message消息。2.在子线程中创建Message消息并调用主线程中的Handler通过sendMessage发送给主线程。

Handler机制中的主要类

Message

Message用来封装线程之间的消息。Message会存储在MessageQuenue中,Message也是链表中的结点。
主要有以下数据

    public int what; //定义任务类型
    public int arg1; //发送Int类型消息
    public int arg2;
    public Object obj;//发送任意类型的消息
    /*package*/ Handler target; //用来处理消息的Handler对象
    /*package*/ Runnable callback;//用来处理消息的回调
     Message next;//指向下一个Message
     public long when;//注意,这是不是发送消息的时间,而是消息应该被处理的时间
     private static Message sPool;//用来存储使用过的Message对象
     private static int sPoolSize = 0;
     private static final int MAX_POOL_SIZE = 50;
Message的消息池设计

官方建议使用消息池来获取Message对象。消息池使用链表实现

private static final Object sPoolSync = new Object();//消息池的锁
private static Message sPool; //消息池
private static int sPoolSize = 0;//消息池中回收的消息数量
private static final int MAX_POOL_SIZE = 50;//消息池最大容量

sPool被static修饰, static 变量有以下特点:

  • 在类装载的时候进行初始化。
  • 多个实例的 static变量会共享同一块内存区域。
    所有Message对象共享这个消息池
    在使用消息池获得消息时,都会调用无参的obtain()方法。具体代码如下:
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next; //将消息池更新到下一个
                m.next = null;
                m.flags = 0; // 重新标识当前Message没有使用过
                sPoolSize--;
                return m;
            }
        }
        return new Message();//消息池为空,就直接创建一个新Message返回
    }

在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; //表示当前Message消息已经被使用过了
        //情空 Message中的数据
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;
        //将当前清空数据的的Message加入到消息池中
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;//头插法,当前节点指向之前消息池的header
                sPool = this;//将消息池中的header替换为当前Message对象
                sPoolSize++;
            }
        }
    }

Handler核心方法

boolean sendMessage(@NonNull Message msg) :发送一个即时消息到消息队列,内部调用的是sendMessageDelayed(msg,0)
boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) :发送一个延时消息,当前时间+delayMillis时间后,handleMessage接收到消息,内部调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
        msg.target = this; //给Message设置发送消息的Hanlder
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        // 如果此Handler是异步的,则发送的所有消息都是异步的。
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //核心步骤,将Message加入到消息队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }

在这里插入图片描述

boolean post(@NonNull Runnable r) :将Runnable中的任务在Handler线程中执行,内部调用 sendMessageDelayed方法
在这里插入图片描述

MessageQueue

消息队列,用来存储Message消息,内部通过单链表实现。
为什么需要一个MessageQueue?
Handler在SendMessage时,会有延时消息,需要等待到指定时间发送,这个就需要MessageQueue来处理,存入和取出逻辑。Message需要延迟处理,那么MessageQueen应该通过时间戳的大小来顺序存储,时间戳小的Message放到队列的头部。

MessageQueue何时被初始化?

MessageQueue并不需要我们手动初始化,Handler中mQueue是来自于Looper,由Looper在构造函数中初始化。

    /**
     * 插入队列
     * @param msg message
     * @param when Handler handleMessage 时间
     * @return false 插入失败 成功
     */
    boolean enqueueMessage(android.os.Message msg, long when) {

        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        //加锁,线程同步,因为Handler可能在不同线程中调用这个方法
        synchronized (this) {
            //message 是 in-use状态时,是不允许入列
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
            ///如果当前MessageQueen已经退出抛出异常并释放Message
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle(); //回收Message并放入Message回收池
                return false;
            }
            //将Message设置为in-use状态
            msg.markInUse();
            //设置Message应该被处理的时间
            msg.when = when;
            //获取MessageQueen的Header
            Message p = mMessages;
            //是否需要唤醒nativeWake方法
            boolean needWake;
            //p == null  代表链表为空
            // when == 0 表示要立即处理该消息
            //when < p.when 表示Message的时间戳比当前队列的Header的时间戳小,那么应该插入到链表最前面
            //满足以上任意一个条件,应该将该msg插入到队列头部
            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 {
               //需要唤起nativeWake,需要满足一下3个条件:线程已经被阻塞&&Handler为空&&异步消息
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                android.os.Message prev;
                //通过for循环将,当前msg插入到合适的位置中
                for (;;) {
                    prev = p;//从队列的Header开始查找
                    p = p.next;
                    //p == null 表示到队列末尾
                    //when < p.when 时,msg应该插入到p端的前面
                    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;
    }

enqueueMessage方法做了哪些工作
1.判断需要入列的消息是否满足要求:1)msg.target != null 2)msg.isInUse()为false 3)MessageQueen的mQuitting 为 false
2.按照Message中的when加入队列中
3.判断是否需要调用nativeWake

既然有存消息入队列,那么也一定有方法取出消息,取出消息通过next方法

Message next() {
        //如果消息循环已经退出并被处理,请返回此处
        //mPtr是从native方法中得到的NativeMessageQueue地址
        //如果mptr等于0说明队列不存在或者被清除掉了
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        //待处理的IdleHandler数量,因为代表数量,所以只有第一次初始化时为-1
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        //线程将被阻塞的时间
        //-1 一直被阻塞
        //0 : 不阻塞
        //>:0 :阻塞nextPollTimeoutMillis毫秒
        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;
                //判断队列头是不是同步拦截器
                if (msg != null && msg.target == null) {
                    //如果是拦截器就向后找一个异步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //判断队列是否有可以取出的消息
                if (msg != null) {
                    if (now < msg.when) { //当前时间小于msg执行的时间
                        // Message未到执行时间,计算线程需要堵塞的时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 已经到执行时间,可以直接取出Message
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        //将msg行消息队列中剥离出来
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        //设置in-use状态
                        msg.markInUse();
                        //返回取出的消息,结束循环,结束next()方法
                        return msg;
                    }
                } else {
                    // 消息队列为空,,nextPollTimeoutMillis为-1,让线程一直阻塞
                    nextPollTimeoutMillis = -1;
                }

                // 如果队列已经退出了直接注销和结束方法
                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.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new android.os.MessageQueue.IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final android.os.MessageQueue.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.
            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.
            nextPollTimeoutMillis = 0;
        }
    }

从代码上面可以看出,当MessageQueen中没有合适的Message出列,会堵塞线程直到有合适的Message出列。

Looper

Looper翻译过来就是循环器,我们上文已经分析了,消息载体(Message),消息队列(MessageQueen),那么如何从MessageQueen中取出消息并分发给MessageQueen?
这个部分工作由Looper完成。
初始化

   private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper的构造函数是private的,外部无法通过构造函数直接创建Looper对象。Looper的对象是在prepare(boolean quitAllowed)方法中创建的

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//判断是否已经存在Looper对象
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //将Looper对象存储到ThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }

从上面代码可以看出Looper有以下特点:
1.一个线程只能有一个Looper
2.Looper对象是放到ThreadLocal中。
在这里插入图片描述
我们平常在主线程中使用Handler时并没有初始化Looper,在主线程中,何时进行初始化操作?
主线程中Looper的初始化操作是在ActivityThread中进行的。

 public static void main(String[] args) {
 /**
 *省略代码
 **/
 Looper.prepareMainLooper();
  /**
 *省略代码
 **/
  Looper.loop();
 }

可以看到主线程是调用Looper.prepareMainLooper()创建了Looper,而且不需要用户手动创建,在Activity启动时就已经创建了,子线程是需要通过prepare()手动创建Looper,并会创建一个MessageQueen能够退出的消息队列。

public static void prepare() {
        prepare(true);
    }

Looper创建后,怎么启动Looper读取MessageQueen?ActivityThread中调用了Looper.loop(),loop()方法就是循环读取MessageQueue中的Message

    public static void loop() {
        //获取当前线程的Looper对象
        final Looper me = myLooper();
        //如果线程中的Looper还未初始化,抛出异常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //一个线程中只能调用一次loop()
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;

        //得到当前线程的唯一标识(uid+pid),作用是下面每次循环时都检查一下线程有没有被切换
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        me.mSlowDeliveryDetected = false;
        //开启死循环
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) { //返回false,就结束循环
                return;
            }
        }
    }

loop() 方法主要是就是开启死循环,调用loopOnce方法

    private static boolean loopOnce(final Looper me,
                                    final long ident, final int thresholdOverride) {
        //从MessageQueue中获取需要处理的Message,获取过程中可能会阻塞线程
        android.os.Message msg = me.mQueue.next(); // might block
        //消息为空,说明队列已经退出了,直接结束循环.结束方法
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }

        /**
         * 中间省略部分代码,只要是记录分发时间和进行性能追踪,防止分发时间过慢
         */
        //向Handler分发数据
        try {
            //调用Handler的dispatchMessage方法
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            //通知Observer消息分发结束
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                // 通知Observer消息分发异常
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            // 恢复当前线程的Uid
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                // 结束追踪
                Trace.traceEnd(traceTag);
            }
        }
        
        //如果本次循环所在的线程和最开始的不一样,打印日志
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }
        //消息分发完毕,回收消息到缓存池
        msg.recycleUnchecked();

        return true;
    }

Looper的功能很简单,核心方法Looper.loop()就是不断地从消息队列中分发对应的宿主Handler,它与对应的MessageQueue息息相关,一起创建,一起退出。
Looper中使用ThreadLocal保证每一个线程只有一个Looper的实例;

整体流程

在这里插入图片描述
Handler发送Message到MessageQueue,Looper通过loop从MessageQueue中获取到时间的Message发送给Handler处理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值