Handler机制源码

Handler机制原理初步分析:

handler机制涉及:handler,Looper,Messager和MessagerQueue

Looper属于线程私有的,通过LocalThreadMap来达到Looper和线程一对一的关系。Looper会不停的去MessagerQueue里面取Messager,然后执行handler的handlerMessage方法。

handler和Looper属于n对1的关系,handler在创建的时候会根据当前线程和对应的Looper做绑定。然后handler就可以发送Messager到对应Looper的MessageQueue里面,Messager则是一个对消息的封装类,它持有handler的句柄,所以handler容易内存泄漏也是因为如此,在消息很多的情况下,Messager创建如果通过new的方式创建,会造成内存抖动,所以通过obain方式创建。

MessagerQueue:是一个队里,先进先出,内部结构是链表结构,方便插入和删除。

源码深入分析:

1.关于Looper的创建:我们主要分析两个方法prepareMainLooper()和prepare()

prepare(), 如果ThreadLocal里面没有对应的Looper,就重新new,并更新到ThreadLocal里面,ThreadLocal是线程私有内存,堆里面开辟了一块内存区域,这个区域属于线程私有。

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

Looper构造函数里面对MessageQueue也相应的做了初始化。

Deprecated
The main looper for your application is created by the Android environment, so you should never need to call this function yourself.

prepareMainLooper()方法有这么一段注释,说明主线程的Loop一开始就存在了。不需要手动执行。

简单做个总结:

1.子线程如果要使用handler机制,那么需要手动调用prepare(),主线程不用。

2.Looper对象存在线程私有内存,和线程一对一绑定。

2.看下Looper的loop方法:

public static void loop() {
        /..省略部分代码../

        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }


private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }

        /..省略部分代码../
        
        try {
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        
        /..省略部分代码../
        return true;
    }

我们看到message为null的时候,loop方法停止,当拿到msg,就执行msg的target的dispatchMessage的方法。如果设置了Observer,则会执行observer回到,我们可以设置observer去捕获一些异常。

3.再看下messageQueue的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();
            }

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

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

这里加了同步关键字,主要防止插入msg和取msg得时候出现并发错误。

拿到msg之后判断了target是否是null,这里需要注意下只有同步屏障的消息target才是null,这里我们看到,如果是屏障消息就循环遍历去拿异步消息,优先执行异步消息,屏蔽普通消息。

之后会去判断when,执行时间是否正常,如果要等就计算出等待时间,然后触发线程等待。

最后在空闲时间执行IDEHandler。

同步屏障:

同步屏障用来保证异步消息第一时间被执行,比如view的绘制的时候,messageQueue里面有很多任务等待执行,如果不及时处理绘制任务,会导致界面卡顿。

1.同步屏障必须要及时取消,否则普通消息不能执行。

2.如果当前页面不停的执行重绘,会导致IDEHandler无法执行。

IDEHandler:

用来执行低优先级的任务,比如我们可以把一些内存的数据在空闲时更新到缓存,下次取得时候更快一点,当然如果没更新成功,也不影响使用,只不过会影响一点点体验。

4.看下messageQueue的插入消息部分

代码不贴了,主要是遍历链表,按照when的从小到大顺序插入合适的位置。然后执行nativeWake来唤醒。

5.handler相关:

public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

上面我们看到,优先执行msg的callback,然后执行handler里面的callback,如果返回true则不执行handlermessage,false情况下会再处理handlerMessage方法。

6.message:

message主要看下如何解决内存抖动的,这段代码我们看到,Message内部管理了一个sPool,最大数量为50,不通过new的方式,通过从池子里面拿message对象,然后被拿走之后会置为null,并计数器减1。

public static 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;
            }
        }
        return new Message();
    }

然后去Looper的Loop方法里面看到

msg.recycleUnchecked();

会将已经处理的msg放到池子里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值