欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android的消息机制

程序员文章站 2024-02-14 16:43:46
一、简介 android的消息机制主要是指handler的运行机制,那么什么是handler的运行机制那?通俗的来讲就是,使用handler将子线程的message放入主...

一、简介

android的消息机制主要是指handler的运行机制,那么什么是handler的运行机制那?通俗的来讲就是,使用handler将子线程的message放入主线程的messagequeue中,在主线程使用。

二、学习内容

学习android的消息机制,我们需要先了解如下内容。

  1. 消息的表示:message
  2. 消息队列:messagequeue
  3. 消息循环,用于循环取出消息进行处理:looper
  4. 消息处理,消息循环从消息队列中取出消息后要对消息进行处理: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层方法.

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!