返回介绍

关系

发布于 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 和您的相关数据。