Android多线程-Handler原理简单剖析

上一章的学习我们已经了解了Handler进行线程间通信的使用流程,我们知道,有几个比较重要的概念需要我们重点去了解,它们分别是:

概念 定义 作用
主线程(UI Thread) 应用程序启动时,默认开启UI线程 处理UI相关操作
子线程(Worker Thread) 人为控制开启的线程 处理耗时操作(网络请求、数据加载等)
消息(Message) 线程间通讯的数据载体 存储线程间需要传递的信息
消息队列(MessageQueue) 存储Message的一个数据结构(先进先出FIFO) 存储Handler发送的消息
处理器(Handler) 线程通信媒介、线程间消息处理器 发送消息到消息队列、处理Looper分发的消息
轮询器(Looper) 消息队列和处理器的通信桥梁 循环取出消息队列中的消息、分发消息给相应的handler

简单剖析

//定义handler
Handler handler = new Handler();
//发送消息
handler.post(new Runnable() {
     @Override
        public void run() {
            //do something
        }
});

首先我们打开Handler的无参构造器源码,调用了两个参数的构造方法,如下:

public Handler(Callback callback, boolean async) {
    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;
}

可以看到先调用了Looper的myLooper方法,并判断了获取对象是否为null,我们继续来看Looper 代码:

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

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

这里牵涉到了ThreadLocal的相关内容,我对这块了解的也不是很多,所以就不献丑了,以后可能会针对Java多线程专门做研究,这里我们只需要知道myLooper返回的对象如果为空,说明该线程没有初始化Looper,handler使用会报错,那么为什么我们在主线程中使用Handler没有出问题呢,这是因为我们来看这些代码:

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

我们追溯这行代码可以看到在ActivityThread类的main方法中调用了此代码,主线程的Looper就是在这里进行绑定的,这也就是为什么我们可以正常使用handler的原因。

接下来我们来看第二部分代码,也就是handler.post的代码部分:

public final boolean post(Runnable r)
{
    return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}

可以看到,该方法实质上还是调用了sendMessage方法,只不过传入的Runnable对象设置为Message的callback。从这过程中也能看出,如果此线程的Looper未能初始化,那么MessageQueue也为空,在sendMessageAtTime方法中依然会报错。下面我们来看看enqueueMessage方法,看名字就是知道是一个入队的方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到在这个方法里给传入的Message设置了Target属性为当前Handler,并将消息根据消息时间放入消息队列中。

到这里我们发送的消息就已经放入消息队列中了,那么怎样取出消息并且发送给Handler呢,我们继续打开Looper的代码并找到这一行:

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
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

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

我们刚刚在查看ActivityThread代码是可以发现main方法中调用了Looper.loop方法,从方法名上看,这是一个循环,从代码看也确实如此。这里有一个无限循环方法,不断地从MessageQueue取出消息,若消息为空,则阻塞,若消息不为空则调用了Message中target的dispatchMessage方法,消费该message后,回收。我们知道这个target也就是Handler,我们来看看这个方法做了哪些事情:

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

这里做了一个判断,在Message有回调方法时,使用Message的回调方法来处理,若回调方法不存在,则调用handleMessage并将message传入此方法,进行进一步处理。

在这个例子里我们知道message的callback是我们传入的runnable,那么会执行handleCallback方法,也就是:

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

如果是我们上一节中的写法,会调用handleMessage方法,也就是我们自己复写的方法,会执行我们自己写的逻辑流程。

至此,我们的流程分析就结束了,当然,只是从皮毛上对Handler 进行了解释,我们这里进行总结:

子线程向主线程发送消息:

  1. 主线程中创建Handler,并复写相应方法,或使用post(Runnable)方式,写下逻辑代码
  2. 子线程中使用Handler的sendMessage或post发出消息
  3. Handler内部处理消息,并将消息传递给MessageQueue,MessageQueue根据消息执行时间进行相应的入队操作
  4. Looper不断轮询从MessageQueue中取出消息,如无消息,就阻塞,若有消息,将消息分发给相应的Handler
  5. Handler收到分发的消息,使用相应的方法进行处理,到这里子线程与主线程通信结束

ps:还有几点重要的补充知识:

子线程若要向子线程发送消息,需要在子线程中调用Looper.prepare、Looper.loop才能使用Handler,不然会报错

一个线程中只能有一个Looper和一个MessageQueue,但可以有多个Handler

好了,这一章的内容就是这些,下一节我们来学习下Android中为解决多线程通信而提供给我们使用的HandlerThread,敬请期待~
我的个人博客,欢迎来访~
enjoy~

猜你喜欢

转载自blog.csdn.net/xiaomi987/article/details/80052380
今日推荐