热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android异步消息处理机制实现原理详解

这篇文章主要介绍了Android异步消息处理机制实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

消息处理机制主要对象:Looper,Handler,Message(还有MessageQueue和Runnable)

Looper不断从MessageQueue消息队列中取出一个Message,然后传给Handle,如此循环往复,如果队列为空,那么它会进入休眠。

这些类的主要变量

Looper.java

static final ThreadLocal sThreadLocal = new ThreadLocal();
  private static Looper sMainLooper; // guarded by Looper.class

  final MessageQueue mQueue;
  final Thread mThread;

Handler.java

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

Message.java

Handler target;每个消息只能对应一个handler
Runnable callback;回调接口

MessageQueue.java

Message mMessages;

Runnable是一个空接口类,没有变量

上一个书上的图:

Handler和Thread没有直接关系,但对应关系可以推理得到

每个Thread只对应一个Looper;

每个Looper只对应一个MessageQueue;

每个MessageQueue对应N个Message,每个Message只对应一个Handler

==》每个Thread对应N个Handler。

Handler是”真正处理事情“的地方,作用:处理消息,将Message压入MessageQueue中

带着一个问题看源码:创建handler对象的线程(ui/主线程除外)为什么,必须先调用Looper.prepare() ?

public Handler() {
    this(null, false);
  }
public Handler(Callback callback) {
    this(callback, false);
  }
public Handler(Looper looper) {
    this(looper, null, false);
  }
 public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
  }
 public Handler(boolean async) {
    this(null, async);
  }
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
      final Class<&#63; extends Handler> klass = getClass();
      if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
          (klass.getModifiers() & Modifier.STATIC) == 0) {
        Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
          klass.getCanonicalName());
      }
    }

    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;
  }
 public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchrOnous= async;
  }

初始化handler对象时(构造方法是Handler(),Handler(Callback callback))都间接调用Handler(Callback callback, boolean async)构造方法

主要代码是Looper.myLooper();

static final ThreadLocal sThreadLocal = new ThreadLocal();//这是在Looper类中的定义
public static Looper myLooper() {
    return sThreadLocal.get();//从当前线程中获得looper对象
  }
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));//为当前线程设置looper对象
}

我们自己创建线程必须通过Looper.prepare()方法为当前线程设置looper对象才可以通过Looper.myLooper()方法返回looper对象,这样在非UI线程创建handler对象时才不会报错。"Can't create handler inside thread that has not called Looper.prepare()"

ps:prepare(boolean quitAllowed)(这个不用我们关心,略过。。)

这个quitAlowed参数是定义消息队列用了,看的源代码是android4.4

Looper.javaprivate Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mRun = true;
    mThread = Thread.currentThread();
  }MessageQueue.java
// True if the message queue can be quit.  private final boolean mQuitAllowed;//true消息队列可以被quit,false消息队列不能被quit。

主线程/UI线程的MessageQueue不能被销毁掉。看源码(销毁调用Looper.quit())

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
      if (sMainLooper != null) {
        throw new IllegalStateException("The main Looper has already been prepared.");
      }
      sMainLooper = myLooper();
    }
  }

偏离太远了

所以得出结论:创建handler对象的线程(ui/主线程除外),必须先调用Looper.prepare()

Handler作用1:处理消息

在Looper类中处理消息是通过msg.target.dispatchMessage(msg);target就是handler对象(Message类的内部变量Handler target)将消息转发到处理消息的对应的handler对象上,然后这个target即handler对象会在处理消息前做一个检查

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//如果msg有绑定callback回调接口Runaable不为空,则执行Runnable的run方法
      handleCallback(msg);
    } else {
      if (mCallback != null) {//如果handler的内置接口类Callback不为空,则执行boolean handleMessage(Message msg)这个方法
        if (mCallback.handleMessage(msg)) {执行完成则return
          return;
        }
      }
      handleMessage(msg);//最后才执行handler本身的方法
    }
  }
 private static void handleCallback(Message message) {    message.callback.run();  }
public interface Callback {//handler的内置接口类Callback
public boolean handleMessage(Message msg);
  }

Handler作用2:将Message压入MessageQueue中

handler中提供的很多发送message的方法,除了sendMessageAtFrontOfQueue()方法(直接调用enqueueMessage(queue, msg, 0);)之外,其它的发送消息方法最终都会辗转调用到sendMessageAtTime()方法

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()方法也是调用Handler中的enqueueMessage(queue, msg, uptimeMillis)方法

和sendMessageAtFrontOfQueue()方法两者最后都会调用enqueueMessage(queue, msg, uptimeMillis)方法

区别是需要延迟uptimeMillis时间后才将Message压入MessageQueue中

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//给msg的target赋值为handler自身然后加入MessageQueue中
    if (mAsynchronous) {
      msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
  }

最终所有的方法都是调用MessageQueue中的enqueueMessage(msg, uptimeMillis);方法,是不是感觉两个方法差不多啊,注意参数!!

MessageQueue的使用是在Looper中

Handler的作用整理完毕(好像我现在已经可以把Handler源码完整默写下来了。哈哈^.^记忆力真不行)

Looper类

作用:与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。

对于Looper主要是prepare()和loop()两个方法

prepare()将普通线程转化为looper线程,

loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

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("<<<<
            var cpro_id = "u6885494";

        
        
    
推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 【Windows】实现微信双开或多开的方法及步骤详解
    本文介绍了在Windows系统下实现微信双开或多开的方法,通过安装微信电脑版、复制微信程序启动路径、修改文本文件为bat文件等步骤,实现同时登录两个或多个微信的效果。相比于使用虚拟机的方法,本方法更简单易行,适用于任何电脑,并且不会消耗过多系统资源。详细步骤和原理解释请参考本文内容。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
author-avatar
渊博的蓝天大海_210
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有