Handler消息机制的源码深度分析

Handler消息机制的源码深度分析

说起Handler机制,难免会提到四大类Handler、MessageQueue、Message、Looper.

我们先不从代码说起,而是从我们的习惯用法说起,在我们使用Handler的时候,普遍用法如下:

Handler myHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

我们来看看Handler的构造方法:

public Handler() {
    this(null, false);
}
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;
}

我们看这句代码

mLooper = Looper.myLooper();

好了,我们主角之一Looper闪亮登场了。Looper类其实是一个调度者的角色,它负责循环处理消息。我们来看一下Looper.myLooper的源码:

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

单单这么写代码,获取到的Looper肯定为Null。那么平时我们在Activity中这么写代码,就没有问题呢,主要是因为Activity线程已经给Handler机制做了准备工作,我们知道Activity运行在UI线程中,也叫做主线程或Main线程,该线程启动的入口是在ActivityThread.main方法中:

public static void main(String[] args) {
    .....
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    .......
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

看到两行标红的代码了吧,第一句Looper.prepareMainLooper(),我们看一下源码:

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
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));
}

在prepare方法中,sThreadLocal被设置了Looper对象,本质上是把当前线程和该looper对象作为一个键值对存放到sThreadLocal,如下代码所示:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

这样在调用Looper.myLooper的时候就能获取到当前线程的Looper对象了。

Looper对象创建完之后,还需要调用Looper.loop方法来开始工作,UI线程在ActivityThread.main方法中已经主动调用了该方法(如上面第二个红行),所以开发者就不用再去手动调用了,我们可以在Main线程中直接创建Handler就行了,而不用考虑Looper的问题。相反地,如果我们是在非UI线程中创建的Handler,则需要手动调用Looper.prepare()方法和Looper.loop()方法,默认情况下,此handler对象是使用的当前子线程的Looper对象,消息处理也是在当前子线程中做的。如果我们想在子线程中发送消息,然后在UI线程中处理,如果我们的Handler是在子线程中创建的,我们可以用Looper.getMainLooper()方法获取到UI线程的looper对象,然后传给Handler,如下面方式创建:

Handler myHandler = new Handler(Looper.getMainLooper()){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

回到正题,继续说Looper.loop()方法,这个工作主要就是利用死循环来不断监听消息并处理,我们看一下源码:

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;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

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

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        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();
    }
}

根据代码可看出,loop()方法的大致逻辑就是不断地从queue中取Message,然后调用msg.target.dispatchMessage方法,不出意外地就是会最终调用到自己创建的handler.handleMessage方法。好了,除了Handler和Looper,另外两个主角MessageQueue和Message也出现了。MessageQueue对象是在创建Looper的时候创建的,它是包含在Looper对象里面的。如下面所示:

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

Handler在发送消息的时候,不管直接调用的是哪个API,最终调用的是sendMessageAtTime方法,我们来看一下该方法源码:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

根据上面源码可以看出,handler发送消息就是把一个Message放入到mQueue里面,等待Looper从中取出来处理,而这个mQueue就是与handler绑定的looper里面的MessageQueue。而上面enqueueMessage方法,可以看出,msg.target会自动赋值为当前Handler对象的引用,也就是说是哪个handler发送的消息,该消息的target就是哪个handler,这个与Looper.loop方法里面的某句代码也对应上了。

到这里整个Handler机制就说完了,Handler里面还有异步地概念,这个不怎么常用,感兴趣的同学可以去研究研究。



阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_27196493/article/details/80930384
文章标签: Handler 消息机制
个人分类: Handler
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭