返回介绍

关系

发布于 2024-12-23 21:36:39 字数 5970 浏览 0 评论 0 收藏 0

Looper: 是一个消息轮训器,他有一个叫 loop() 的方法,用于启动一个循环,不停的去轮询消息池。

这里插一句,Looper 中的 loop() 方法是如何实现阻塞的呢?可查看 Android-Discuss issue 245

  • MessageQueue:就是上面说到的消息池
  • Handler:用于发送消息,和处理消息
  • Message:一个消息对象

现在要问了,他们是怎么关联起来的?

一切都要从 Handler 的构造方法开始。如下所示

final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;

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;
  mAsynchronous = async;
  }

可以看到 Handler 本身定义了一个 MessageQueue 对象 mQueue,和一个 Looper 的对象 mLooper。

不过,对 Handler 的这两个成员变量的初始化都是通过 Looper 来赋值的。

mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;

现在,我们新建的 Handler 就和 Looper、MessageQueue 关联了起来,而且他们是一对一的关系,一个 Handler 对应一个 Looper,同时对应一个 MessageQueue 对象。这里给 MessageQueue 的赋值比较特殊,

mQueue = mLooper.mQueue;

这里直接使用 looper 的 mQueue 对象,将 looper 的 mQueue 赋值给了 Handler 自己,现在 Looper 和 Handler 持有着同一个 MessageQueue 。

这里可以看到 Looper 的重要性,现在 Handler 中的 Looper 实例和 MessageQueue 实例都是通过 Looper 来完成设置的,那么下面我们具体看看 Looper 是怎么实例化的,以及他的 mQueue 是怎么来的。

Looper

从上面 Handler 的构造方法中可以看到,Handler 的 mLooper 是这样被赋值的。

  mLooper = Looper.myLooper();

接着看 myLooper() 的实现。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}

这里出现了一个平时不怎么看到的 ThreadLocal 类,关于这个类,推荐去阅读任玉刚的一篇文章 - Android 的消息机制之 ThreadLocal 的工作原理,讲的很不错。另外自己也写了一篇文章,用于讲解 ThreadLocal 的用法,以及他在 Handler 和 Looper 中的巧妙意义。

任玉刚 - Android 的消息机制之 ThreadLocal 的工作原理

这里他是通过 ThreadLocal 的 get 方法获得,很奇怪,之前我们没有在任何地方对 sThreadLocal 执行过 set 操作。 这里却直接执行 get 操作,返回的结果必然为空啊!

但是如果现在为空,我们在 new Handler() 时,程序就已经挂掉了啊,因为在 Handler 的构造方法中,如果执行 Looper.myLooper() 的返回结果为空。

mLooper = Looper.myLooper();
if (mLooper == null) {
   throw new RuntimeException(
     "Can't create handler inside thread that has not called Looper.prepare()");
}

但是,我们的程序没有挂掉,这意味着,我们在执行 myLooper() 方法时,他返回的结果不为空。

为什么呢?那我们在 Looper 中看看,哪里有对应的 set 方法,如下所示,我们找到了一个全局静态方法 prepare

public static void prepare() {
  prepare(true);
}

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));
}

我们看到,在最后一行这里,执行了对应的 set 操作,这里把一个 new 出来的 Looper 直接 set 到 sThreadLocal 中。

但是我们不知道,到底什么时候,是谁调用了 prepare() 方法,从而给 sThreadLocal 设置了一个 Looper 对象。

后来在网上经过搜索,找到了答案,我们的 Android 应用在启动时,会执行到 ActivityThread 类的 main 方法,就和我们以前写的 java 控制台程序一样,其实 ActivityThread 的 main 方法就是一个应用启动的入口。

在这个入口里,会做很多初始化的操作。其中就有 Looper 相关的设置,代码如下

public static void main(String[] args) {

  //............. 无关代码...............

  Looper.prepareMainLooper();

  Looper.loop();

  throw new RuntimeException("Main thread loop unexpectedly exited");
}

在这里,我们很清楚的看到,程序启动时,首先执行 Looper.prepareMainLooper() 方法,接着执行了 loop() 方法。

先看看 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();
  }
}

public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}

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));
}

这里,首先调用了 prepare() 方法,执行完成后,sThreadLocal 成功绑定了一个 new Looper() 对象,然后执行

sMainLooper = myLooper();

可以看看 sMainLooper 的定义,以及 myLooper() 方法;

private static Looper sMainLooper;  // guarded by Looper.class  

public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}

现在的 sMainLooper 就有值了,也就是说,只要我们的 App 启动,sMainLooper 中就已经设置了一个 Looper 对象。以后调用 sMainLooper 的 get 方法将返回在程序启动时设置的 Looper,不会为空的。

下面在看 main 方法中的 调用的 Looper.loop() 方法。 已经把一些无关代码删了,否则太长了,

public static void loop() {

  //获得一个 Looper 对象
  final Looper me = myLooper();

  // 拿到 looper 对应的 mQueue 对象
  final MessageQueue queue = me.mQueue;

  //死循环监听(如果没有消息变化,他不会工作的) 不断轮训 queue 中的 Message
  for (;;) {
    // 通过 queue 的 next 方法拿到一个 Message
    Message msg = queue.next(); // might block
    //空判断
    if (msg == null)return;
    //消息分发   
    msg.target.dispatchMessage(msg);
    //回收操作  
    msg.recycleUnchecked();
  }
}

现在,想一个简单的过程,我们创建了一个 App,什么也不做,就是一个 HelloWorld 的 Android 应用, 此时,你启动程序,即使什么也不干,按照上面的代码,你应该知道的是,现在的程序中已经有一个 Looper 存在了。 并且还启动了消息轮询。 Looper.loop();

但是,目前来看,他们好像没什么用,只是存在而已。

此时你的项目如果使用了 Handler,你在主线程 new 这个 Handler 时,执行构造方法

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;
  mAsynchronous = async;
}

此时的 myLooper() 返回的 Looper 就是应用启动时的那个 Looper 对象,我们从 Looper 的构造方法得知,在 new Looper 时,会新建一个对应 的消息池对象 MessageQueue

private Looper(boolean quitAllowed) {
     mQueue = new MessageQueue(quitAllowed);
     mThread = Thread.currentThread();
}

那么在 Handler 的构造方法中,那个 mQueue 其实也是在应用启动时就已经创建好了。

现在再来回顾一下 Handler 的构造方法,在构造方法中,他为自己的 mQueue 和 mLooper 分别赋值,而这两个值其实在应用启动时,就已经初始化好了。

并且,现在已经启动了一个消息轮训,在监听 mQueue 中是不是有新的 Message ,现在这个轮训器已经好了,我们看发送消息的过程。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文