Android由简到深之Handler

因为是由浅到深的讲解,我会在高级部分做专门的标注,基础部分是必须记住的部分,高级部分根据个人情况进行掌握,现在开始

Handler在新手时期是非常好用的,它是android.os包下的一个类,主要用处有以下几点

1、线程切换

2、延时操作

3、消息队列

4、界面间通信

这几个功能决定了基本任何中大型项目都会用到Handler,我们的讲解也会围绕这几个点展开

1、线程切换

最基本的用法,在主线程里可以这样调用

   Handler().post { 
            // 执行UI线程操作
        }

一句简单的代码就能让你轻松实现任何线程切换到主线程的操作,主要用于子线程耗时操作完成后的界面刷新,比如网络接口请求后,能力引擎调用后

当然,我们不要频繁的new Handler(),我们可以声明一个handler,在需要的时候使用

注意,我们说的是线程切换,不是切换到主线程,意思是,Handler也能切换到子线程

下面以一个工具类举例 首先看用法,简洁明了,获取后台线程的Handler和UI线程的Handler

 工具类的实现如下

public class TaskRunner {
    private static final String TAG = "TaskRunner";

    private Handler mUIHandler;

    private Handler mBackHandler;

    private TaskRunner() {
        mUIHandler = new Handler(Looper.getMainLooper());
    }

    private static class SingletonHolder {
        static TaskRunner sRunner = new TaskRunner();
    }

    public static synchronized Handler getUIHandler() {
        return SingletonHolder.sRunner.mUIHandler;
    }

    public static synchronized Handler getBackHandler() {
        Handler backHandler = SingletonHolder.sRunner.mBackHandler;
        if (null == backHandler) {
            HandlerThread handlerThread = new HandlerThread(TAG);
            handlerThread.start();
            backHandler = new Handler(handlerThread.getLooper());
            SingletonHolder.sRunner.mBackHandler = backHandler;
        }

        return backHandler;
    }
}

可以看到,我们创建主线程的Handler,在有可能是子线程使用的情况下,不是单纯的new Handler(),而是使用new Handler(Looper.getMainLooper()),目前我们只需要记住这个写法,具体原因会在后续高级部分中讲解

同理,切换到子线程,使用new Handler(handlerThread.getLooper())进行初始化 这个HandlerThread也会在后续讲解

这个切换工具类十分便捷,作为开发的常用工具使用

2、延时操作

Handler().postDelayed({
            // 延迟两秒做一些事情
        }, 2 * 1000)

 延时一定时间后执行,并且不阻塞主线程,这个方法很重要,主要用处如下

a、定位问题时,如果猜想是时序问题,可以使用延时执行某段代码来确认

b、解决频繁调用问题-这个涉及到消息队列,讲第三点的时候会解释

c、交互上要求需要延时一定时间,可以使用

但使用不熟练的话不建议在正式项目中使用,因为可能引起大问题

a、延迟操作可能会出现界面退出被回收时执行了刷新操作,导致空指针崩溃

解决方法:在界面Destroy时,调用handler的removeMessage(0)来移除所有延时操作

b、频繁调用会在消息队列中不停插入消息,导致界面更新缓慢或者卡顿

解决方法:不要频繁调用,或者remove上一个消息

3、消息队列

首先我们要知道,在我们持有一个handler的时候,我们的post,postDelay,发送的事件,Handler是不会立刻响应的,它们会在一个队列里,Handler会不断取出我们发送的事件,一个个处理,那么我们再来看,这个流程涉及到了哪些类

a、Handler的消息队列涉及哪些类,他们又是怎样工作的?

主要有三个类:

Looper 字面意思是循环,是我们上面说的,不断取出消息队列中事件的方式

MessageQueue 是我们上面说的消息队列,它只是一个队列,可以存储Message

Message 是我们上面说的事件,我们的post,postDelay方法,发送的就是Message

即:Handler从消息队列(MessageQueue 不断循环(Looper 取出我们发送的事件(Message ,一个个处理

【高级部分】详解三个类的实现

a、Message 消息,它本质上是一个消息对象类,它主要有三个变量

target:就是负责发送且分发它的Handler;
callback:是一个Runnable回调;
next:下一个要执行的message

b、MessageQuenue 消息队列,它本质上是一个消息管理类

mMessage是一个Message对象,上面说过,Message的next标记了下一个要执行的Message,所以MessageQuenue持有了Message的单链表,并接收和处理消息

通过 boolean enqueueMessage(Message msg, long when)  函数接收消息

boolean enqueueMessage(Message msg, long when) {
        // 校验Handler
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
    
        // 简单的线程同步
        synchronized (this) {
            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();
                return false;
            }

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

我们来仔细分析下这段代码

可以看到,for循环里会比对消息发送的时间,并按时间先后顺序执行

注意,这里调用的native方法nativeWake()唤醒取出消息的next()

当消息入队时候,如果队列为空的或需要立即分发当前消息分发时间-早于消息队列中最近一个需要分发的消息的时间的(而此时MessageQueue的next()方法的循环是处于阻塞状态的),就调用一个native方法nativeWake()唤醒取出消息的next()

通过 Message 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.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
              //-1-直接休眠一定的时间;
            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;
                  //-2-找出下一个需要分发的消息。
                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) {
                    //-3- 如果时机未到,即当前时间还不到消息的处理时间,就计算休眠时间,
                    //反之,返回这个待处理的消息。并将休眠状态mBlocked置为false
                    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;
                }

                // Process the quit message now that all pending messages have been handled.
                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.
                //-4-只有当消息队列是空的或者现在还不到待处理的消息的时间的时候,idleHanler(空闲handler)才会被执行
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //-5-如果没有空闲Handler待处理(消息队列也没有需要处理的message),那么,标记一下循环被阻塞了。
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
                //-5-将idleHandler列表转为数组保存
                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.

            //-6-开始处理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);
                }

                //-7-如果IdleHandler的queueIdle返回false,就不需要保持,这执行完一次任务后直接移除,反之,
                //后面每次空闲的时候都执行 
                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;
        }
    }

这里调了native的休眠方法——nativePoolOnce()(正好对应了消息入队的nativeWake()),如果没有消息处理的时候,next()方法就会阻塞在这里,而阻塞却没有导致当前线程ANR的原因,是因为native的nativePoolOnce方法时候,调用了linux低层,使当前线程腾出CPU的使用权,处于休眠等待状态,直到休眠时间结束或消息入队被唤醒。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值