【转载】每个 Android 开发者必须知道的消息机制问题总结
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 开发者必须知道的消息机制问题总结的更多相关文章
- (转载)Android开发者必知的开发资源
(转载)http://www.importnew.com/3988.html 随着Android平台市场份额的持续猛增 ,越来越多的开发者开始投入Android应用程序的开发大潮.如果您是一位2013 ...
- Android 面试收集录5 消息机制
1.消息机制概述 1.1.消息机制的简介 在Android中使用消息机制,我们首先想到的就是Handler. 没错,Handler是Android消息机制的上层接口. Handler的使用过程很简单, ...
- android 进程/线程管理(四)----消息机制的思考(自定义消息机制)
关于android消息机制 已经写了3篇文章了,想要结束这个系列,总觉得少了点什么? 于是我就在想,android为什么要这个设计消息机制,使用消息机制是现在操作系统基本都会有的特点. 可是andro ...
- [Android]简略的Android消息机制源码分析
相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.jav ...
- 深入理解 Handler 消息机制
记得很多年前的一次面试中,面试官问了这么一个问题,你在项目中一般如何实现线程切换? 他的本意应该是考察 RxJava 的使用,只是我的答案是 Handler,他也就没有再追问下去了.在早期 Andro ...
- Android开发系列之事件拦截机制
对于Android开发者来说理解事件传递机制的重要性,我想应该是不言而喻的.在一个Activity里面,我们经常会重写onTouchEvent事件,可是重写结束之后,对于是返回true还是返回fals ...
- [转载]Android开发者必须深入学习的10个应用开源项目
[转载]Android开发者必须深入学习的10个应用开源项目 原文地址:Android开发者必须深入学习的10个应用开源项目(http://blog.sina.com.cn/s/blog_7b8a63 ...
- Android 高级UI设计笔记08:Android开发者常用的7款Android UI组件(转载)
Android开发是目前最热门的移动开发技术之一,随着开发者的不断努力和Android社区的进步,Android开发技术已经日趋成熟,当然,在Android开源社区中也涌现了很多不错的开源UI项目,它 ...
- 【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什 ...
随机推荐
- 墙上你APP设计与实现
墙上你APP,听名字你们就应该可以想到是一个类似于表白墙吧,其实在做这个项目的时候我就是看到我们学校有一个专门工程墙,但是你找它发布消息表白或者找寻自己在学校掉了的饭卡或者身份证的时候,需要等它们同意 ...
- 常用类-excel转csv
public class ExcelFileHelper { public static bool SaveAsCsv(string excelFilePath, string destination ...
- SpringBoot启动项目时提示:Error:java: 读取***.jar时出错;
场景 在IDEA中新建SpringBoot项目后,修改了默认的Maven仓库和配置文件,然后在启动项目时提示: Error:java: 读取\org\assertj\assertj-core\3.11 ...
- .WrongArgumentException: Malformed database URL, failed to parse the connection string near ';characterEncoding=UTF-8&;serverTimezone=Asia/Shanghai'.)
连接mysql库报的异常信息: org.springframework.transaction.CannotCreateTransactionException: Could not open JDB ...
- SSM框架之Mybatis(5)数据库连接池及事务
Mybatis(5)数据库连接池及事务 1.Mybatis连接池 Mybatis 中也有连接池技术,但是它采用的是自己的连接池技术.在 Mybatis 的 SqlMapConfig.xml 配置文 ...
- Dynamics 365 Online-Delete Audit History Data
Dynamics 365 CE自带的Audit功能,虽然不会给我们的业务流程带来显著变化,但是这个功能对于我们追溯数据变化的历史,诊断定制触发的执行,以及数据还原等,都是不可或缺的关键先生.尤其是涉及 ...
- 中文版 Apple 官方 Swift 教程《The Swift Programming Language》
简介 欢迎使用 Swift 关于 Swift 版本兼容性 Swift 初见 Swift 版本历史记录 Swift 教程 基础部分 基本运算符 字符串和字符 集合类型 控制流 函数 闭包 枚举 类和结构 ...
- PHP时间戳相互转换
1.获取当前时间方法date()很简单,这就是获取时间的方法,格式为:date(format,format,timestamp),format为格式.timestamp为时间戳–可填参数. 2.获取时 ...
- Phoenix核心功能原理及应用场景介绍以及Calcite 查询计划生成框架介绍
Phoenix是一个开源的HBase SQL层.它不仅可以使用标准的JDBC API替代HBase Client API创建表,插入和查询HBase,也支持二级索引.事物以及多种SQL层优化. 此系列 ...
- linux驱动——cmdline原理及利用【转】
转自:https://blog.csdn.net/qingzhuyuxian/article/details/82895416 最近安卓项目中想要获取内核cmdline特定的启动参数,因为我们在他的U ...