Android的消息机制
一、简介
android的消息机制主要是指handler的运行机制,那么什么是handler的运行机制那?通俗的来讲就是,使用handler将子线程的message放入主线程的messagequeue中,在主线程使用。
二、学习内容
学习android的消息机制,我们需要先了解如下内容。
- 消息的表示:message
- 消息队列:messagequeue
- 消息循环,用于循环取出消息进行处理:looper
- 消息处理,消息循环从消息队列中取出消息后要对消息进行处理:handler
平常我们接触的大多是handler和message,今天就让我们来深入的了解一下他们。
三、代码详解
一般而言我们都是这样使用handler的
xxhandler.sendemptymessage(xxx);
当然还有其他表示方法,但我们深入到源代码中,会发现,他们最终都调用了一个方法
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); }
sendmessageattime()方法,但这依然不是结束,我们可以看到最后一句enqueuemessage(queue, msg, uptimemillis);按字面意思来说插入一条消息,那么疑问来了,消息插入了哪里。
boolean enqueuemessage(message msg, long when) { if (msg.target == null) { throw new illegalargumentexception("message must have a target."); } if (msg.isinuse()) { throw new illegalstateexception(msg + " this message is already in use."); } synchronized (this) { if (mquitting) { illegalstateexception e = new illegalstateexception( msg.target + " sending message to a handler on a dead thread"); log.w(tag, e.getmessage(), e); msg.recycle(); return false; } 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。
虽然我们一般把他叫做消息队列,但是通过研究,我们发下,它实际上是一种单链表的数据结构,而我们对它的操作主要是插入和读取。
看代码33-44,学过数据结构,我们可以轻松的看出,这是一个单链表的插入末尾的操作。
这样就明白了,我们send方法实质就是向messagequeue中插入这么一条消息,那么另一个问题随之而来,我们该如何处理这条消息。
处理消息我们离不开一个重要的,looper。那么它在消息机制中又有什么样的作用那?
looper扮演着消息循环的角色,具体而言它会不停的从messagequeue中查看是否有新消息如果有新消息就会立刻处理,否则就已知阻塞在那里,现在让我们来看一下他的代码实现。
首先是构造方法
private looper(boolean quitallowed) { mqueue = new messagequeue(quitallowed); mthread = thread.currentthread(); }
可以发现,它将当前线程对象保存了起来。我们继续
looper在新线程创建过程中有两个重要的方法looper.prepare() looper.loop
new thread(){ public void run(){ looper.prepare(); handler handler = new handler(); looper.loop(); } }.start();
我们先来看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)); }
咦,我们可以看到这里面又有一个threadlocal类,我们在这简单了解一下,他的特性,set(),get()方法。
首先threadlocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储后,只有在制定线程中可以获取存储的数据,对于其他线程而言则无法获取到数据。简单的来说。套用一个列子:
private threadlocal<boolean> mbooleanthreadlocal = new threadlocal<boolean>();// mbooleanthreadlocal.set(true); log.d(tah,"threadmain"+mbooleanthreadlocal.get()); new thread("thread#1"){ public void run(){ mbooleanthreadlocal.set(false); log.d(tah,"thread#1"+mbooleanthreadlocal.get()); }; }.start(); new thread("thread#2"){ public void run(){ log.d(tah,"thread#2"+mbooleanthreadlocal.get()); }; }.start();
上面的代码运行后,我们会发现,每一个线程的值都是不同的,即使他们访问的是同意个threadlocal对象。
那么我们接下来会在之后分析源码,为什么他会不一样。现在我们跳回prepare()方法那一步,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 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(); } }
首先loop()方法,获得这个线程的looper,若没有抛出异常。再获得新建的messagequeue,在这里我们有必要补充一下messagequeue的next()方法。
message next() { // return here if the message loop has already quit and been disposed. // this can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mptr; if (ptr == 0) { return null; } int pendingidlehandlercount = -1; // -1 only during first iteration int nextpolltimeoutmillis = 0; for (;;) { if (nextpolltimeoutmillis != 0) { binder.flushpendingcommands(); } nativepollonce(ptr, nextpolltimeoutmillis); synchronized (this) { // try to retrieve the next message. return if found. final long now = systemclock.uptimemillis(); message prevmsg = null; message msg = mmessages; if (msg != null && msg.target == null) { // stalled by a barrier. find the next asynchronous message in the queue. do { prevmsg = msg; msg = msg.next; } while (msg != null && !msg.isasynchronous()); } if (msg != null) { if (now < msg.when) { // next message is not ready. set a timeout to wake up when it is ready. nextpolltimeoutmillis = (int) math.min(msg.when - now, integer.max_value); } else { // got a message. mblocked = false; if (prevmsg != null) { prevmsg.next = msg.next; } else { mmessages = msg.next; } msg.next = null; if (debug) log.v(tag, "returning message: " + msg); msg.markinuse(); return msg; } } else { // no more messages. nextpolltimeoutmillis = -1; } // process the quit message now that all pending messages have been handled. if (mquitting) { dispose(); return null; } // if first time idle, then get the number of idlers to run. // idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingidlehandlercount < 0 && (mmessages == null || now < mmessages.when)) { pendingidlehandlercount = midlehandlers.size(); } if (pendingidlehandlercount <= 0) { // no idle handlers to run. loop and wait some more. mblocked = true; continue; } if (mpendingidlehandlers == null) { mpendingidlehandlers = new idlehandler[math.max(pendingidlehandlercount, 4)]; } mpendingidlehandlers = midlehandlers.toarray(mpendingidlehandlers); } // run the idle handlers. // we only ever reach this code block during the first iteration. for (int i = 0; i < pendingidlehandlercount; i++) { final idlehandler idler = mpendingidlehandlers[i]; mpendingidlehandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueidle(); } catch (throwable t) { log.wtf(tag, "idlehandler threw exception", t); } if (!keep) { synchronized (this) { midlehandlers.remove(idler); } } } // reset the idle handler count to 0 so we do not run them again. pendingidlehandlercount = 0; // while calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextpolltimeoutmillis = 0; } }
从24-30我们可以看到,他遍历了整个queue找到msg,若是msg为null,我们可以看到50,他把nextpolltimeoutmillis = -1;实际上是等待enqueuemessage的nativewake来唤醒。较深的源码涉及了native层代码,有兴趣可以研究一下。简单来说next()方法,在有消息是会返回这条消息,若没有,则阻塞在这里。
我们回到loop()方法27msg.target.dispatchmessage(msg);我们看代码
public void dispatchmessage(message msg) { if (msg.callback != null) { handlecallback(msg); } else { if (mcallback != null) { if (mcallback.handlemessage(msg)) { return; } } handlemessage(msg); } }
msg.target实际上就是发送这条消息的handler,我们可以看到它将msg交给dispatchmessage(),最后调用了我们熟悉的方法handlemessage(msg);
三、总结
到目前为止,我们了解了android的消息机制流程,但它实际上还涉及了深层的native层方法.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
推荐阅读
-
Android编程实现仿美团或淘宝的多级分类菜单效果示例【附demo源码下载】
-
深入讲解基于JDK的动态代理机制
-
Android ContentProvider的实现及简单实例代码
-
Android 如何修改APK的默认名称
-
Android实现基于ViewPager的无限循环自动播放带指示器的轮播图CarouselFigureView控件
-
Android之沉浸式状态栏的实现方法、状态栏透明
-
Android隐藏标题状态栏的方法
-
Android使用selector修改TextView中字体颜色和背景色的方法
-
Android实现带附件的邮件发送功能
-
Android viewpager中动态添加view并实现伪无限循环的方法