概述
线程间共享数据一般采用:全局变量方式、消息传递方式、参数传递方式等。
而在Android系统中采用最多的则是消息传递方式,也就是我们说的Android消息机制。Android消息机制设计的本意就是实现线程间通信。对于每个Android应用程序都运行在一个dalvik虚拟机进程中,应用进程开始的时候会启动一个主线程(MainThread),主线程负责处理和UI相关的事件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。并且主线程为我们提供了消息循环的机制,我们可以利用这个机制来实现线程间的通信。那么,我们就可以在非UI线程发送消息到UI线程,最终让UI线程来进行UI的操作。同时我们也可以实现子线程之间的通信,从而执行特定动作。但是当有多个后台任务时且异步任务的数据不太大时,我们自己创建消息循环来实现就有点复杂了,这时我们就可以使用Handler来完成了,由于主线程天然就具有消息循环机制,只需要创建Handler,重写handleMessage或者发送一个runnable即可。这就是我们为何要引入Handler消息机制。
那么什么是Handler机制?如何使用Handler机制?使用Handler机制常见问题有哪些?这是我们大家所关心的,所以本文后面会主要就这几个方面展开来讲解。
主要组成部分
在分析Handler消息机制之前首先了解一下它主要的组成部分:
Handler: 用来将消息发送到消息队列,对消息进行分发,并且对消息进行处理。
Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue。并且实现消息循环。
MessageQueue(消息队列):用来存放消息,并且与底层交互。
Message:消息对象,用来传递数据信息。
线程间通信
有A,B两个线程,我们可以在线程B中创建多个Handler,在线程A中用对应Handler的引用向消息队列(MessageQueue)中发送消息(Message)。线程B中的Looper会对MessageQueue进行管理,并且将消息从消息队列中取出,然后交由对应的Handler进行分发与处理,从而实现线程间通信。
主线程与子线程通信
在线程间通信中我们用的最多的就是在子线程中执行耗时任务,如查询数据库等,然后将执行结果发送到主线程中进行操作。首先我们在子线程中获得主线程的Looper对象,然后将执行结果封装成一个Message对象,通过Handler对象将其发送到主线程的消息队列中。主线程的Looper将消息从消息队列中取出,并在主线程中处理。
Android应用程序的消息处理机制是由消息循环、消息发送和消息处理这三个业务流程组成的,接下来,我们就详细描述这三个过程。
消息循环
消息循环的过程分为,创建消息循环过程与进入消息循环过程,下面我们就分析一下这两个过程: 消息循环是由Looper类来实现的,所以在讲应用程序进入消息循环之前我们应该首先分析一下应用程序是如何创建Looper对象的。我们可以通过调用Looper.prepare函数来为线程创建一个Looper对象,该函数定义在frameworks/base/core/java/android/os/Looper.java文件中:
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对象是存放在sThreadLocal成员变量里面的,成员变量sThreadLocal的类型为ThreadLocal,表示这是一个线程局部变量,保证每一个调用prepare的线程都有一个独立的Looper对象。我们在创建Looper的时候同时也会创建一个MessageQueue对象。后续的消息都会存放在该消息队列中,线程消息队列在Android应用程序消息处理机制中的作用比较重要,因此,我们看看它的构造函数的实现,代码定义在frameworks/base/core/java/android/os/MessageQueue.java文件中:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
它的初始化工作是在JNI中实现的。在JNI中会创建一个NativeMessageQueue对象,然后会将该对象返回到Java层中记录,从而实现Java层与JNI层的交互。代码位置在frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
N