被人问到:在Android中除去主线程之外的线程间通信怎么做,顿时一脸蒙比,貌似还真没做过,也许是当时脑子短路,或者说是对Android里面线程间通信理解不够,没反应过来,其实和有没有主线程是没有关系的。

切入点

大家都知道,一般在耗时操作完成的时候,如果要通知主线程更新UI,可以向主线程里面的Handler发送更新UI的消息,然后主线程去更新UI,这样就完成了一次简单的主线程和其他线程间的通信,具体过程如下:

android.app.ActivityThread#main中有如下简化操作:

...
public static void main(String[] args) {
	...
	Looper.prepareMainLooper();
	
	ActivityThread thread = new ActivityThread();
	thread.attach(false);
	
	if (sMainThreadHandler == null) {
	    sMainThreadHandler = thread.getHandler();
	}
	...
	// End of event ActivityThreadMain.
	Looper.loop();
	
	throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到,在应用启动时,初始化了一个ActivityThread ,然后这个ActivityThread创建的时候初始化了一个Handler,并将这个Handler通过thread.getHandler()返回给了sMainThreadHandler

消息接收

至此主线程中的Handler就有了,那它是怎么接收消息的

回到上面的简化版代码中,可以看到:

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

我们看看Looper.prepareMainLooper()到底干了什么

/**
 Initialize the current thread as a looper, marking it as an
 application's main looper. The main looper for your application
 is created by the Android environment, so you should never need
 to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

注释里面写的很清楚,这是Android环境来调用的,会为应用根据当前线程创建一个main looper,而里面是通过myLooper()返回了一个Looper对象,在prepare()里面准备了一些参数,先看看prepare()方法

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

/**
 * Initialize the current thread as a looper.
 * This gives you a chance to create handlers that then reference
 * this looper, before actually starting the loop. Be sure to call
 * {@link #loop()} after calling this method, and end it by calling
 * {@link #quit()}.
 */
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));
}

prepare()里先判断了一下sThreadLocal.get()里面有没有存入Looper,如果有则抛出异常,因为一个线程只能有一个Looper被创建;如果没有的话则创建一个不可被退出Looper,记得前面在prepareMainLooper()里面调用的时候是prepare(false)

继续看Looper的构造函数

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

看起来很简单,创建了一个消息队列,并与当前的线程绑定起来,这样通过myQueue() getThread() myLooper()可以获取消息队列、绑定的线程以及当前线程的Looper

/**
 Gets this looper's message queue.
 *
 @return The looper's message queue.
 */
public @NonNull MessageQueue getQueue() {
    return mQueue;
}

/**
 Gets the Thread associated with this Looper.
 *
 @return The looper's thread.
 */
public @NonNull Thread getThread() {
    return mThread;
}

/**
 Return the Looper object associated with the current thread.  Returns
 null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

至此主线程的Looper就被创建好了,然后再看Looper.loop()的简化版代码:

/**
 Run the message queue in this thread. Be sure to call
 {@link #quit()} to end the 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;
    ...
    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 me,然后通过me.mQueue获取绑定的消息队列queue,然后就进入死循环了,线程阻塞,等待消息过来,所以一旦queue.next()返回了消息这里就能处理

那消息又是怎么过来的呢,消息的并发又是怎么处理的?

可以看看queue.next()里面的简化版代码:

Message next() {
    for (; ; ) {
        ...
        nativePollOnce(ptr, nextPollTimeoutMillis);
        
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            ...
            Message msg = mMessages;
            if (msg != null) {
                ...
                // Got a message.
                msg.next = null;
                msg.markInUse();
                return msg;
            }
        }
    }
}

查阅资料后知道,如果消息队列中有消息,nativePollOnce方法会将消息保存在mMessages成员中,一旦nativePollOnce拿到了一条新的消息,检查mMessages成员变量是否为空,不为空则返回mMessages,然后在loop()方法中,通过返回的Message对象的target属性拿到与之绑定的Handler,并通知Handler去处理这条消息。

消息发送

消息接收搞明白了,再看看消息发送

如果咱们想要给主线程发送消息,一般来说会这么干

Handler handler = new Handler(Looper.getMainLooper()) {
	@Override
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
		switch (msg.what) {
			case MSG_SOMETHING:
				// do something
				break;
		}
	}
}

通过主线程的Looper初始化了一个Handler,重写其handleMessage(Message msg)方法处理对应消息,而发送消息则是调用此HandlersendMessageAtTime方法(最终调用的都是这个):

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

可以看到sendMessageAtTime是调用了MessageQueue.enqueueMessage(),而此时,将this赋予了msg.target,上面消息接受成功后处理消息的target就是这个时候绑定上去的。

MessageQueue.enqueueMessage()做的事情就是把消息插入到消息队列中合适的位置(按消息触发时间排序),然后判断是否需要通知,如果需要则调用native方法MessageQueue.nativeWake()通知Handler处理。

总结

所以从主线程和其他线程间的通信过程发现,其实和是不是主线程没有什么关系,只不过主线程中的HandlerLooper是由Android的环境给初始化好了的。

最后再看一下官方推荐的写法

class LooperThread extends Thread {
     public Handler mHandler;
   
     public void run() {
         Looper.prepare();
   
         mHandler = new Handler() {
             public void handleMessage(Message msg) {
                 // process incoming messages here
             }
         };
   
         Looper.loop();
     }
 }

于是又有了疑问,我记得Handler有一个以Looper作为参数的构造函数,为什么这里不需要?于是再看Handler构造函数里面都写了什么

public Handler() {
    this(null, false);
}

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

至此终于明白Android中线程间通信的原理。