Android的消息机制

作者:青色的画轴 时间:2023-08-05 10:19:28 

一、简介

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层方法.

来源:http://www.cnblogs.com/yrstudy/p/6358783.html

标签:Android,消息机制
0
投稿

猜你喜欢

  • 解决jasperreport导出的pdf每页显示的记录太少问题

    2023-04-14 13:02:01
  • 详解Java内部类与对象的打印概念和流程

    2021-10-10 21:36:56
  • Spring activiti如何实现指定任务处理者

    2023-04-08 05:53:16
  • C++版本基于ros将文件夹中的图像转换为bag包

    2021-11-13 07:15:59
  • Android使用WebView实现全屏切换播放网页视频功能

    2021-10-19 18:21:16
  • JAVA SFTP文件上传、下载及批量下载实例

    2023-02-11 14:31:46
  • C#使用yield关键字让自定义集合实现foreach遍历的方法

    2022-07-21 11:44:28
  • Android编程使用android-support-design实现MD风格对话框功能示例

    2022-05-19 03:46:39
  • 详解APP微信支付(java后台_统一下单和回调)

    2023-11-10 17:26:42
  • springmvc图片上传及json数据转换过程详解

    2022-02-25 17:11:14
  • IDEA设置背景为自定义照片的操作方法

    2022-12-28 09:13:08
  • java返回json请求中文变成问号的问题及解决

    2023-11-01 02:56:34
  • C#装箱与拆箱操作的深入讲解

    2023-04-29 19:10:06
  • Android 支付宝支付、微信支付、银联支付 整合第三方支付接入方法(后台订单支付API设计)

    2023-08-28 01:36:01
  • Spring Boot JPA如何把ORM统一起来

    2023-04-12 21:26:14
  • java中最易犯错的特殊字符示例详解

    2022-09-27 17:51:08
  • Flutter Drawer抽屉菜单示例详解

    2022-07-30 12:34:11
  • Jenkins一键打包部署SpringBoot应用的方法步骤

    2021-10-31 18:00:53
  • newtonsoft.json解析天气数据出错解决方法

    2022-03-10 12:23:21
  • Textvie实现左边图片和换行文字左对齐的方法

    2022-10-30 23:15:16
  • asp之家 软件编程 m.aspxhome.com