第一章:Android 消息机制
1.1 消息机制概述
Android 消息机制由 Handler、Looper 和 MessageQueue 三者协作完成,用于在单线程内实现异步任务调度(如主线程更新 UI)。该机制可分为四个过程:
- 消息机制初始化
- 消息轮询
- 消息发送
- 消息处理
其底层基于 Linux 的 epoll(I/O 事件通知机制) 和 eventfd(事件通知文件描述符) 实现。
【补充】可将整个消息机制想象成“邮局系统”:
- Message = 信件
- MessageQueue = 邮筒(按投递时间排序)
- Looper = 邮递员(不断检查邮筒)
- Handler = 寄件人 & 收件人
- epoll/eventfd = 邮局的“有信提醒铃”
1.2 消息机制初始化
- 从
Looper.prepare()开始(通常在主线程自动调用); - 调用 Looper 构造函数,创建 Looper 实例,并存入线程私有变量
ThreadLocal; - Looper 构造函数中创建
MessageQueue; -
MessageQueue构造方法调用nativeInit()(JNI 方法),初始化 Native 层消息队列; - Native 层
MessageQueue创建时,调用 NativeLooper构造函数; - Native
Looper构造函数调用rebuildEpollLocked(); -
rebuildEpollLocked()中:- 调用
epoll_create1()创建 epoll 实例(返回一个文件描述符); - 调用
epoll_ctl()将 唤醒事件文件描述符(eventfd) 注册到 epoll 监听列表。
- 调用
【补充】epoll vs select/poll:
select/poll每次都要遍历所有监听的文件描述符,效率随数量下降;epoll将“注册”和“等待”分离,内部用红黑树管理,性能稳定。
- epoll 实例内部包含两个关键结构:
-
rbr(红黑树根节点):存储所有被监控的事件(高效插入/查找); -
rdlist(就绪链表):当事件发生时,内核将其加入此链表,供epoll_wait()返回。
-
1.3 eventfd 唤醒机制
- 唤醒事件文件描述符为
eventfd对象(Linux 特有); - 相比传统
pipe,eventfd支持 广播式通知(多个线程可同时 wait,write 一次全部唤醒),而 pipe 仅支持点对点; -
eventfd_ctx结构体包含:-
wqh:等待队列头结点(带自旋锁的双向链表,用于挂起等待线程); -
count:64 位无符号整数计数器(每次 write 增加,read 读取并清零)。
-
【补充】
eventfd就像一个“全局计数器铃”:
- 写入一次(如写 8 字节的 1),计数器 +1;
- 所有在
epoll_wait中等待该 fd 的线程都会被唤醒。
1.4 消息轮询过程
- 从
Looper.loop()开始(进入死循环); -
loop()调用MessageQueue.next()获取下一条待处理消息; -
next()调用nativePollOnce(timeout)(JNI 方法); - 调用链深入 Native 层:
nativePollOnce()→NativeMessageQueue.pollOnce()→Native Looper.pollOnce(); - 最终调用
epoll_wait(timeout):-
若消息队列为空且无延时消息,
timeout = -1,线程进入永久阻塞状态; - 关键点:此阻塞由内核管理,不消耗 CPU,因此 Looper 死循环不会导致 ANR;
-
若消息队列为空且无延时消息,
-
epoll_wait返回后(因新消息到达或超时),MessageQueue检查消息类型:- 若遇到同步屏障(Sync Barrier),则跳过所有同步消息,优先返回异步消息(如 UI 绘制 invalidate);
- 否则返回下一条已到达发送时间的非异步消息。
【补充】同步屏障是 Android 保证 UI 流畅的关键机制:
当系统需要立即绘制(如点击按钮),会插入一个“屏障”,临时屏蔽普通消息,让绘制消息插队执行。
1.5 消息发送过程
- 从
Handler.sendMessage()或post(Runnable)开始; - Handler 调用
MessageQueue.enqueueMessage(msg, when)将消息按时间顺序插入队列; -
MessageQueue 本质是单链表(非环形队列),按
when(触发时间戳)排序; -
enqueueMessage()逻辑:- 若
msg.when >= 链表尾节点.when,直接插入尾部; - 否则遍历链表找到插入位置;
- 判断是否需要唤醒轮询线程:
- 需要唤醒条件:新消息是队列首条,或为异步消息且存在同步屏障;
- 若需唤醒,调用
nativeWake()(JNI 方法); -
nativeWake()→NativeMessageQueue.wake()→Native Looper.wake(); -
Native Looper.wake()通过write(eventfd, &W, sizeof(W))向 eventfd 写入一个值; - 内核检测到 eventfd 可读,立即唤醒阻塞在
epoll_wait的线程。
- 若
1.6 消息处理过程
-
Looper.loop()从next()获取消息后,调用msg.target.dispatchMessage(msg); -
msg.target即发送消息时的 Handler 实例; -
Handler.dispatchMessage()执行顺序:- 若
msg.callback != null(如通过post(Runnable)发送),则执行msg.callback.run(); - 否则调用
handleMessage(msg)(开发者重写的业务逻辑)。
- 若
1.7 最佳实践与常见问题
-
Message 复用:务必使用
Message.obtain()从全局池获取对象,避免频繁创建 GC; -
IdleHandler:通过
Looper.myQueue().addIdleHandler()添加,用于主线程空闲时执行轻量任务(如预加载); -
Handler 内存泄漏:
- 非静态内部 Handler 会隐式持有外部 Activity 引用;
- 若消息未及时处理,Activity 无法回收 → 内存泄漏;
-
解决方案:
- 使用 静态内部 class + WeakReference;
- 或在
Activity.onDestroy()中调用handler.removeCallbacksAndMessages(null)。
-
ANR 风险:
handleMessage()在主线程执行,耗时操作会导致 ANR; -
异步消息优先级:
- 系统可通过
Handler(async=true)创建异步 Handler(应用层不可用); - 异步消息在分发时优先于同步消息;
-
同步屏障由系统在
ViewRootImpl.scheduleTraversals()中插入,确保 UI 绘制不被普通消息阻塞。
- 系统可通过
【补充】消息处理顺序(Native 层):
- 处理 Native Message(如 Input 事件)
- 处理 Native Request(如 Choreographer 回调)
- 处理 Java Message(Handler 消息)
这解释了为何“Java 层消息少,但 UI 仍卡顿”——底层事件可能积压。
📚 深入 Handler 机制:Android 消息机制
📚 Handler 原理详解:Handler消息机制
第二章:进程间通信(IPC)机制
2.1 Linux 进程通信方式
- 管道(Pipe):单向、1v1、数据一次性读取;
- 共享内存:双向、最快(无需拷贝)、无同步机制、多对多;
- Socket:基于文件接口,双向、多对多、需两次数据拷贝、支持跨主机。
2.2 Android IPC 方式:Binder
- 内核空间:操作系统独占的内存区域(全系统仅一份);
- 用户空间:每个进程独立的虚拟内存区域(多份);
-
虚拟内存:程序使用虚拟地址编程,由 MMU(Memory Management Unit,内存管理单元) 硬件将虚拟地址翻译为物理地址;
- MMU 是硬件芯片,非软件模块;
- 可设置内存页的读写/执行权限,实现进程隔离与安全。
2.3 虚拟内存管理
- 虚拟内存划分为固定大小块 —— 页(Page),默认 4096 字节(4KB);
- 物理内存划分为 页框(Page Frame),同样 4KB;
- 页表(Page Table):存储虚拟页号 ↔ 物理页框号的映射关系,由 MMU 使用。

2.4 Binder 通信原理
- 传统 IPC(如 Socket)需 两次数据拷贝:
进程A用户空间 → 内核缓冲区 → 进程B用户空间; -
Binder 通过 mmap 减少一次拷贝:
- Binder 驱动在内核创建一块 共享内存缓冲区;
- 发送方进程通过 mmap() 将该缓冲区映射到自己的用户空间;
- 发送方直接写入映射地址 → 数据进入内核缓冲区(一次拷贝);
- 接收方通过 Binder 驱动直接访问同一内核缓冲区(零拷贝)。

📚 深入 Binder 机制:Android 大话binder通信
2.5 mmap 机制详解
- mmap(memory map) 允许程序将文件或设备直接映射到虚拟内存,避免 read/write 系统调用;
- 适用场景:高性能 I/O(如数据库、图形处理);
-
限制:
- 映射大小必须是 页面(4KB)整数倍;
- 面向流的设备(如串口)不支持 mmap;
- 具体实现依赖硬件平台。
-
关键特性:
- mmap 仅分配虚拟地址空间,不立即加载文件内容;
- 首次访问虚拟地址时触发 缺页异常(Page Fault);
- 内核以 页为单位 从磁盘加载数据到物理内存;
- 操作系统可能预读相邻页,提升后续访问速度。

📚 mmap 原理解析:mmap的解析
第三章:AIDL 与跨进程通信
3.1 AIDL 基础
- AIDL(Android Interface Definition Language) 用于定义跨进程接口;
-
支持的数据类型:
- 8 种基础类型(int, long, boolean 等);
- String、CharSequence;
- Parcelable(需实现序列化);
- List、Map(元素类型也需支持 AIDL)。
【补充】AIDL 文件本身不执行任何逻辑,它只是一个接口契约模板。编译时,AAPT 会根据它生成 Java 接口和 Stub/Proxy 类。
📚 AIDL 应用示例:AIDL简单应用
3.2 AIDL 工作原理
- 真正起作用的不是 .aidl 文件,而是系统生成的 Java 代码;
- 服务端创建一个 Service,并在
onBind()中返回 Stub 的实现类; - 客户端通过
bindService()获取 Proxy 对象; - 客户端调用 Proxy 方法 → 通过 Binder 驱动 跨进程调用服务端 Stub 方法;
- 本质:AIDL 是 Binder 通信的语法糖,简化了手动编写 IBinder 接口的工作。
【补充】可将 AIDL 理解为“远程遥控器”:
- 你在客户端按“开机”(调用方法);
- 信号通过红外(Binder)传到电视(服务端);
- 电视执行开机逻辑,并返回状态。
📚 深入 AIDL:AIDL
第四章:Intent 数据传递限制
4.1 TransactionTooLargeException
- 使用 Intent 传递数据时,可能抛出
TransactionTooLargeException; - 根本原因:Intent 携带的数据需通过 Binder 传输,而 Binder 有全局缓冲区大小限制;
-
默认上限:约 1MB(实际为
MAX_TRANSACTION_SIZE = 1 << 20字节); -
关键注意:
- 此 1MB 是整个进程所有 Binder 事务共享的缓冲区,非单次 Intent 限制;
- 不同 Android 版本或厂商 ROM 可能调整此值;
- 即使数据 < 1MB,若系统繁忙也可能触发异常。
【补充】最佳实践:
- 避免通过 Intent 传递 Bitmap、大 List 等;
- 改用 全局单例、文件路径、ContentProvider 或 EventBus 传递大数据。
📚 详情:Intent传递数据大小限制
第五章:Android 四大组件与进程模型
5.1 四大组件特性
- 均可跨进程通信(通过 Intent + Binder);
-
Activity vs Service:
- Activity 是有状态的,状态通过
savedInstanceState表达; - Service 无 UI,无状态(除非自行维护)。
- Activity 是有状态的,状态通过
5.2 Application 与进程生命周期
- 每个进程有且仅有一个 Application 对象;
- 进程启动时,系统依次调用:
-
attachBaseContext()(可在此替换 Context) - 注册所有静态 ContentProvider
Application.onCreate()
-
- 进程存活条件:进程中存在至少一个活跃组件(Activity/Service/BroadcastReceiver/ContentProvider);
- 进程销毁:当所有组件 inactive 且系统内存不足时,系统杀死进程;
-
进程重建:若因 crash 或
System.exit()终止,系统会重建进程并重启最后活跃的 Activity。
📚 Binder IPC 原理:Android IPC原理
📚 跨进程设计案例:shadow的跨进程设计
第六章:BroadcastReceiver 与 Binder 服务
6.1 BroadcastReceiver 原理
- 广播接收机制完全基于 Binder 实现;
- 系统服务 ActivityManagerService (AMS) 维护一个全局的 广播注册表;
-
静态广播(AndroidManifest 注册):
- 即使进程未启动,AMS 也能通过 PMS 启动目标进程;
- 适用于开机启动、网络变化等系统级事件。
-
动态广播(代码 registerReceiver):
- 仅在注册的进程/组件存活时有效;
- 生命周期与注册上下文绑定。
【补充】广播就像“全村喇叭”:
- 系统(村长)喊一声“断网了!”;
- 所有登记过的村民(Receiver)都能听到并行动。
📚 深入分析:BroadcastReceiver广播原理分析
6.2 Binder 核心接口
-
IBinder:跨进程通信的基础接口,定义了transact()方法; -
Parcel:高性能数据容器,用于在 Binder 事务中序列化/反序列化数据; -
Binder:IBinder的具体实现类,服务端继承它提供功能; -
BinderProxy:客户端持有的代理对象,封装了 Binder 驱动调用。
📚 机制详解:Android跨进程通讯机制之Binder、IBinder、Parcel、AIDL
📚 Binder 传递细节:Binder机制
