在android中,android的消息机制对于每一个开发者来说这都是必须要掌握的,因为在日常开发中我们总是避免不了的,最常见的莫过于用来子线程与主线程间的通信,更新UI,在RxAndroid中,我们
AndroidSchedulers.mainThread()
方法返回的也是一个包裹在Scheduler
中的Handler对象。从代码的角度来说,Handler是对android消息队列机制的高层次封装,我们使用的时候只要对Handler进行操作即可。
主线程Handler的创建和使用
public class MainActivity extends AppCompatActivity {
private Handler myHandler = new MyHandler(this);//创建Handler对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//发送handler消息
Message m = myHandler.obtainMessage();
m.obj = "Message content.";
myHandler.sendMessage(m);
}
/**
* 创建Handler的子类,不推荐直接new一个Handler的匿名子类,因为有可能会产生内存泄露
*/
static class MyHandler extends Handler {
private WeakReference<Activity> act;
public MyHandler(Activity act) {
this.act = new WeakReference<Activity>(act);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Toast.makeText(act.get(), msg.obj.toString(), Toast.LENGTH_SHORT).show();
}
}
}
上面就是Handler的使用过程,创建出Handler实例,在handleMessage
方法中处理消息,创建Message
消息对象,然后通过Handler来发送消息。一切都很简单,这主要归功于Handler的高层次封装。下面我们就来看看里边的原理
Hanlder 的创建过程
Handler有很多个构造方法,我们挑一个作为入口就可以了,那么从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;
//......
}
我们会发现它在构造方法中主要做了两件事,调用Looper.myLoop()方法获得一个Looper实例,通过Looper实例获取到一个MessageQueue对象。那么什么是Looper,什么是MessageQueue呢。这就引出了Android真正的消息机制的实现。在Android中,消息循环就是通过Looper、Message和MessageQueue来实现的。
Message : 主要用于承载我们发送的消息内容的对象,我们的数据信息都是放在这个实例里面
MessageQueue : 该类内部维护一个Message单向链表结构,我们发送出Message对象之后,Message对象会被存储在MessageQueue实例内部,等待被处理
Looper: 该类主要的作用是循环的从MessageQueue中取出Message进行处理
接着往下走,先看 Looper.myLooper()
方法做了什么事情
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
在该方法中我们发现它调用了sThreadLocal.get()
来取得Looper实例。sThreadLocal
是维护在Looper 类中的一个静态ThreadLocal 的实例。ThreadLocal 是用来存储一个线程本地变量的类。到了这里或许会有一个疑问,Looper实例是什么时候放进ThreadLocal 中的呢?让我们先保留这个问题接着往下走,在Handler构造方法调用Looper.myLooper()
方法之后会对获得的实例进行空安全检查,当实例为空的时候会抛出一个RuntimeExcption,这个异常信息的描述也告诉了我们,在创建Handler对象之前要调用Looper.prepare()
方法,那我们现在来看一下Looper.prepare()
方法都做了些什么。
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们会发现在Looper.prepare()
方法中创建了Looper实例,同时将它放进ThreadLocal 实例中。在Looper 的构造方法中则是创建了MessageQueue 实例,我们走到MessageQueue的构造方法中
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
我们会发现这边调用了一个nativeInit
的方法,并返回了一个Long
类型的值,并将其赋给mPtr
,我们接着往下走,看看nativeInit
的方法里面都做了什么,mPtr
是一个什么东西,该方法是一个JNI函数,我们可以在android_os_MessageQueue 中找到
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);
}
}
从这里可以看出来,在这边同步的创建了一个native的消息队列NativeMessageQueue
以及在NativeMessageQueue
的构造方法里创建了对应于当前线程的一个Native层面的Looper
对象,最后在android_os_MessageQueue_nativeInit
方法里返回出了这个NativeMessageQueue
的指针,现在我们就可以知道,nativeInit
方法同步的为我们创建一个Native层面的消息队列,mPtr
是指向该队列的指针。到这里就完成了一个Handler的创建过程。
遍观整个过程,我们其实一个疑问,为什么在主线程中,**我们可以直接创建Handler对象而不需要调用Looper.prepare()
方法呢?**这个可以在ActivityThread类中找到答案,android进程的main()
方法就在该类中。
public static void main(String[] args) {
//......
Looper.prepareMainLooper();
//......
Looper.loop();
}
在main
方法里面我们发现系统在这里调用了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();
}
}
在Looper.prepareMainLooper()
方法中我们发现它调用了Looper.prepare()
方法。所以我们不需要在主线程中调用Looper.prepare()
方法
####Handler 发送消息消息的过程
使用Handler发送消息有很多的重载方法,我们就从上文中的myHandler.sendMessage(m)
方法进入,逐层跟进sendMessage
⇒
\Rightarrow
⇒ sendMessageDelayed
⇒
\Rightarrow
⇒sendMessageAtTime
最后一直到enqueueMessage
的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在Handler.enqueueMessage
方法中,给当前的我们发送的Message实例绑定了target
属性,该属性的值就是当前的Handler 实例,然后执行MessageQueue.enqueueMessage
方法
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在MessageQueue.enqueueMessage
方法,我们发送的Message在这里被添加到了消息队列中,在这个方法中,有一个值得注意的方法nativeWake(mPtr);
,这个方法的作用我们放到后面去进行说明,现在先保留在这里。走到这里,我们发送消息的一个过程就已经走完。
发送了一个消息,那总得有一个处理消息的地方,我们现在接着看Android里边是怎么处理Handler发送的消息的。
####Handler处理消息的过程
关于处理Message的一个过程,我们还得回过头,从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;
for (; ; ) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
}finally{}
msg.recycleUnchecked();
}
}
该方法去掉一些不重要的代码之后大致就是这样,首先是调用myLooper()
方法取得当前线程的Handler
和MessageQueue
实例,之后直接进入一个死循环中,然后不停的读MessageQueue里面的Message,并调用Message对象中的target
对象的dispatchMessage()
方法,Message实例的target
对象又是什么呢?它就是我们用来发送Message 信息的那个Handler实例,关于Handler.dispatchMessage()
的实现我们回头再看。我们接着往下走,Handler.dispatchMessage()
方法执行完成之后就调用Message.recycleUnchecked()
清空回收该Message实例,接着就进入下一轮循环。
在这里可能也会有一个疑问,一直不停的循环取出MessageQueue实例中的Message,总有一次会将MessageQueue实例中的Message取完,然后MessageQueue.next()
方法返回null
然后结束循环,那这个消息机制不就停止运行了吗?
关于这个问题,我们就要来看一下MessageQueue.next()
方法的具体实现了。
Message next() {
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
return msg;
}else {
// No more messages.
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
}
}
}
除掉一些逻辑处理的代码最后会发现MessageQueue.next()
方法里面也是一个死循环,当没有消息时,线程会一直阻塞在MessageQueue.next()
方法里面,一直到有新的Message进入队列,并且只有调用MessageQueue.quit()
方法之后才会返回出null。那没有Message的时候,是一直在MessageQueue.next()
方法里面做循环吗,答案是否定的。当没有消息的时候,在调用nativePollOnce
方法是阻塞住,该方法最终会调用到Linux的epoll_wait
函数,并且传入的超时时间为-1,这将会一直阻塞住等待被唤醒。要唤醒它就要提到我们之前说过的nativeWake
的方法了,该方法会执行一个Native的写入操作来唤醒线程,继而开始下一轮的消轮询。
在有消息的情况下,会调用Handler.dispatchMessage()
方法执行我们在发送消息添加的回调方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在这个方法中我们发现它会先判断当前Message实例的callback
属性是否有值,如果有值,则调用Message.callback.run()
方法,该回掉是一个Runable的实例,Handler.post(Runable callback)
方法就是这样实现的,Message实例的callback
属性无值的时候才会去调用Handler.handleMessage()
方法,该方法也就是我们一般处理逻辑的地方
##最后附上一图##