Android的消息机制几乎是面试必问的话题,当然也并不是因为面试,而去学习,更重要的是它在Android的开发中是必不可少的,占着举足轻重的地位,所以弄懂它是很有必要的。下面就来说说最基本的东西。

Looper

作用:

  • 关联起Thread
  • 循环取出消息

1、Looper是否可以直接实例化?

Looper构造方法是私有的,其中做了两件事

  • 创建一个MessageQueue
  • 得到与之对应的Thread
 private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

2、一个线程能对应多个Lopper?

不能,一个线程对应一个Looper对象,通过ThreadLocal保证一个线程只有一个Looper与之对应,如果多次调用Looper.prepare();则会抛出运行时异常。

 private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { // 查看是否有looper与当前线程对应
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

3、Looper是无限循环,会阻塞吗?

是,当开启一个loop后是一个死循环,从MessageQueue中取出消息,处理消息,但是也有可能退出,在没有消息后退出循环。

  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;
// 略
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) { // 当没有消息的时候,退出
// No message indicates that the message queue is quitting.
return;
}
// 略
msg.target.dispatchMessage(msg);
}

4、可以再次调用Looper.prepareMainLooper吗?

不可以,Looper.prepareMainLooper最终也是调用prepare(),同2.

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

5、MainLooper什么时候创建的?

MainLooper是启动Activity创建ActivityThread(并不是一个Thread)时候创建,所以不能多次创建。

public static void main(String[] args) {
// 略
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// 略
ActivityThread thread = new ActivityThread();
thread.attach(false);
// 略
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
} // 略
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}

Handler

作用:

  • 发送消息到MessageQueue
  • 处理消息

1、Handler如何与Looper、MessageQueue关联起来?

我们知道一个Looper对应一个Thread,一个Looper包含一个MessageQueue。当我们创建Handler时就会从当前线程中取出与之对应的Looper,让后在从Looper中取出MessageQueue。

// 1、自动获取
public Handler(Callback callback, boolean async) {
// 略
mLooper = Looper.myLooper(); // 取出当前线程中的Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; // 取出MessageQueue
mCallback = callback;
mAsynchronous = async;
}
// 2、传递一个Looper进来
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

Message

单项链表结构。

作用:

  • 数据的载体

1、消息如何复用的?

从全局消息池(链表结构)中

public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

2、Message为什么能传递?

Android中想要传递对象要么实现Serializable要么Parcelable,在这里是实现了Parcelable接口。

public final class Message implements Parcelable {
// 略
}

3、如何与Handler关联?

我们知道在消息传机制中Handler充当着“快递员”的角色,那么他又是如何与“货物”--Message发生关系呢?实际上Message有一个成员变量target他的类型正是Handler,

/*package*/ Runnable callback;

public int arg1; 

public int arg2;

public Object obj;

/*package*/ Handler target; // 关键点

当我们通过Handler去send一个Message时候最终都会为target赋值为this,即当前的Handler。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 赋值语句
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

另为如果是通过Message.Obtain(),获取的复用Message也会为其赋值。

多说一句,Handler.obtainMessage()调用的就是Message.Obtain()。

public final Message obtainMessage(){
return Message.obtain(this);
}

总结:

通过一系列的包涵关系,最终Looper、Handler、Message、MessageQueue即发生关联,从而形成一个闭合,开启消息循环。

困惑

【转载】每个 Android 开发者必须知道的消息机制问题总结的更多相关文章

  1. (转载)Android开发者必知的开发资源

    (转载)http://www.importnew.com/3988.html 随着Android平台市场份额的持续猛增 ,越来越多的开发者开始投入Android应用程序的开发大潮.如果您是一位2013 ...

  2. Android 面试收集录5 消息机制

    1.消息机制概述 1.1.消息机制的简介 在Android中使用消息机制,我们首先想到的就是Handler. 没错,Handler是Android消息机制的上层接口. Handler的使用过程很简单, ...

  3. android 进程/线程管理(四)----消息机制的思考(自定义消息机制)

    关于android消息机制 已经写了3篇文章了,想要结束这个系列,总觉得少了点什么? 于是我就在想,android为什么要这个设计消息机制,使用消息机制是现在操作系统基本都会有的特点. 可是andro ...

  4. [Android]简略的Android消息机制源码分析

    相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.jav ...

  5. 深入理解 Handler 消息机制

    记得很多年前的一次面试中,面试官问了这么一个问题,你在项目中一般如何实现线程切换? 他的本意应该是考察 RxJava 的使用,只是我的答案是 Handler,他也就没有再追问下去了.在早期 Andro ...

  6. Android开发系列之事件拦截机制

    对于Android开发者来说理解事件传递机制的重要性,我想应该是不言而喻的.在一个Activity里面,我们经常会重写onTouchEvent事件,可是重写结束之后,对于是返回true还是返回fals ...

  7. [转载]Android开发者必须深入学习的10个应用开源项目

    [转载]Android开发者必须深入学习的10个应用开源项目 原文地址:Android开发者必须深入学习的10个应用开源项目(http://blog.sina.com.cn/s/blog_7b8a63 ...

  8. Android 高级UI设计笔记08:Android开发者常用的7款Android UI组件(转载)

    Android开发是目前最热门的移动开发技术之一,随着开发者的不断努力和Android社区的进步,Android开发技术已经日趋成熟,当然,在Android开源社区中也涌现了很多不错的开源UI项目,它 ...

  9. 【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述

    ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什 ...

随机推荐

  1. Android Fragment 隐藏或显示时调用的生命周期方法

    Fragment使用方式大体分两种: 大家要注意不同的Fragment使用方法,Fragment隐藏和显示调用的生命周期方法是不同的,以下是Fragment显示隐藏调用的方法: //判断是否展示—与V ...

  2. 并发容器之ConcurrentLinkedQueue

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  3. PHP mysqli_stmt_bind_param MySQLi 函数

    定义和用法 mysqli_stmt_bind_param - 将变量绑定到准备好的语句作为参数 版本支持 PHP4 PHP5 PHP7 不支持 支持 支持 语法 mysqli_stmt_bind_pa ...

  4. Spring MVC的注解二

    概述 Spring从2.5版本开始引入注解,虽然版本不断变化,但是注解的特性一直被延续下来并不断进行扩展,这里就来记录一下Spring MVC中常用的注解,本文承接前文继续记录@PathVariabl ...

  5. .Net Core MVC中过滤器简介

    在.Net Framework MVC 中有四种过滤器,授权过滤器(Authorize).Action 过滤器.结果过滤器(Result).异常过滤器(Exception)四种过滤器.在.Net Co ...

  6. openstack 搭建

    #所有节点修改ip,主机名和hosts解析 controller 10.0.0.11 controller compute1 10.0.0.31 compute1 #所有节点准备本地repo源 rm ...

  7. spark SQL、RDD、Dataframe总结

  8. PostgreSQL 查询、创建、删除索引

    --查询索引 select * from pg_indexes where tablename='tab1'; --创建索引 tab1_bill_code_index 为索引名, create ind ...

  9. ArcGIS以数据库作为数据源作为source发布服务步骤详解(以Postgresql为例)及各种发布问题

    创建企业级数据库 Data Management Tools-->Geodatabase Administration-->Create Enterprise Geodatabase 按如 ...

  10. RDIFramework.NET敏捷开发框架Web新增邮件中心实现便捷式的邮件收发

    1.引言 邮件收发在很多业务系统中都有这样的需求,是比较正式和常用的功能.在我们的框架中提供了邮件中心功能模块,集内部邮件的收发.邮件归类.邮件星标的标记.邮件的删除与彻底删除等,邮件中心功能模块界面 ...