Handler实现原理

今天在尝试把百度定位sdk的初始化转移到子线程时,遇到了一个问题:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

这个问题并不少见,网上一搜有很多。
解决方法就是在创建handler时传入Looper.getMainLooper()。

但是我发现我在子线程执行的代码里并没有创建handler,只是创建了一个百度sdk的定位类。
显然是在这个类里边创建了handler,导致了这个问题。显然这个方法解决不了我的问题,由于handler并不是我创建的,我并没有办法给它传进去一个looper。

只能看一下后台的实现原理,决定怎么解

首先看下源码,在哪里报的错:

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

可以看到是在handler的构造函数中抛出的异常,从代码和错误内容都不难看出是获取looper失败。那么我们再看一下这个Looper.myLooper()方法:

public static Looper myLooper() {
    return sThreadLocal.get();
}

它只是返回了一个线程里的looper,显然是这个线程里没有looper造成的。线程里的looper是在哪里设置的呢?我们通过错误的内容“has not called Looper.prepare()”可以推断出应该就是prepare()方法了:

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

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。

那么Looper到底是干嘛的呢?
首先我们都知道收到消息都是在handleMessage()方法处理的。我们就从最熟悉的地方找起,handleMessage方法是在哪调的呢:

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

可以看到是在dispatchMessage(Message msg)里调用的。这里先判断有没有callback,也就是Runnable,如果自己写了runnable,那么就交由runnable处理

private static void handleCallback(Message message) {
    message.callback.run();
}

如果没有,则调用handleMessage()方法。

继续往上找, dispatchMessage(Message msg)是在这里调用的:

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        msg.target.dispatchMessage(msg);
        msg.recycleUnchecked();
    }
}

这里有很多代码,我们只捡关心的看,首先他拿到了当前的looper,然后从中获取了一个队列,也就是消息队列。可以知道一个looper里会有一个消息队列。然后是一个死循环,循环获取队列中的message,然后调用之前的dispatchMessage(msg)。这样就把整个过程连起来了:

Looper维持一个消息队列,然后不断的从队列里边拿出消息,然后交给handelr处理。还差一点,就是队列里的消息是从哪来的呢。这个相信用过的人都知道,自然就是sendMessage:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
可以看到这是一个包装,经过一系列的调用链,最终调用到了
queue.enqueueMessage(msg, uptimeMillis)
这个queue就是从looper里获取到的那个消息队列,这个方法里就是一个典型的队列链表操作:

 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 {
            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;
        }
    }
    return true;
}

好了现在就全明白了,handler拿着一个looper,不断地往looper手里的消息队列里放消息,looper拿着这个消息队列又不断地往出取,让handler来处理消息。

至于最开始的问题,已经很简单了。只需要在线程中调用Looper.prepare()在线程中放一个looper,然后在最后执行Looper.loop(),让它不断的往出取消息就可以了。即使不直接往handler里传入looper,handler也会在构建时自动从线程中拿到looper。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值