1.Android 系统运行机制 【Looper】【Choreographer】篇
2.android里面所说的looper是什么意思啊?
3.Androidä¹Looper使ç¨
4.找到卡顿来源,BlockCanary源码精简分析
5.Android组件系列:再谈Handler机制(Native篇)
Android 系统运行机制 【Looper】【Choreographer】篇
Android系统运行机制涉及Looper和Choreographer的关键模块。在系统启动后,进程便进入一个无限循环状态,核心任务是处理输入和输出,这两个过程主要由Looper的筹码分布加出货形态源码消息机制驱动。
Looper中的主要操作是MessageQueue的next方法,它负责接收来自Java层和Native层的消息,包括用户输入事件。在Java层,如果无消息可用,会调用native的pollOnce来处理Native层的消息。屏幕刷新遵循人眼视觉停留机制,Android通过Vsync信号确保每.ms绘制一帧,这一过程依赖于JNI回调Choreographer的Vsync监听。
Choreographer的doFrame方法是关键,每当收到Vsync信号时,它会执行UI线程的核心工作。doFrame执行了一系列操作,包括处理输入事件、办卡源码动画和视图遍历。输入事件首先在Native层处理,然后传递到Java层,如RecyclerView的滑动事件,会触发layout和绘制过程,最后通过SurfaceFlinger显示到屏幕上。
整个流程从Native层捕获输入事件开始,经过JNI传递到Java层,通过Looper和Choreographer协调,确保以Vsync频率更新UI,直至图像绘制并显示给用户。这个机制确保了Android系统的流畅交互体验。
android里面所说的looper是什么意思啊?
Looper即:有消息循环的线程。
在Android里线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新概念。主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,引入一个新的mxnet源码架构机制Handle,有消息循环,就要往消息循环里 面发送相应的消息,自定义消息一般都会有对应的处理,消息的发送和清除,消息的处理,把这些都封装在Handle里面,注意Handle只是针对那些有Looper的线程,不管是UI线程还是子线程,只要有Looper,就可以往消息队列里面添加东西,并做相应的处理。
Androidä¹Looper使ç¨
Looperæ¯Androidä¸çä¸ä¸ªç±»ï¼ç¨äºä¸ºçº¿ç¨æä¾æ¶æ¯å¾ªç¯ãå¨Androidä¸ï¼ä¸»çº¿ç¨å·²ç»é»è®¤å¼å¯äºä¸ä¸ªLooperï¼å æ¤å¯ä»¥ç´æ¥ä½¿ç¨Handleræ¥åéæ¶æ¯ãä½æ¯å¯¹äºå ¶ä»çº¿ç¨ï¼å¦æéè¦ä½¿ç¨Handleræ¥åéæ¶æ¯ï¼å°±éè¦å å建ä¸ä¸ªLooperã
以ä¸æ¯ä½¿ç¨Looperçæ¥éª¤ï¼
1. å¨å线ç¨ä¸å建ä¸ä¸ªLooper对象ï¼å¹¶è°ç¨Looperçprepare()æ¹æ³åLooperçloop()æ¹æ³ï¼è¿æ ·å°±å¯ä»¥ä¸ºè¯¥çº¿ç¨å建ä¸ä¸ªæ¶æ¯å¾ªç¯ã
```java
public class MyThread extends Thread {
public Handler mHandler;
public void run() {
// å建Looper对象
Looper.prepare();
// å建Handler对象
mHandler = new Handler() {
public void handleMessage(Message msg) {
// å¤çæ¶æ¯
}
};
// è¿å ¥æ¶æ¯å¾ªç¯
Looper.loop();
}
}
```
2. å¨ä¸»çº¿ç¨æå ¶ä»çº¿ç¨ä¸ï¼å¯ä»¥éè¿Handlerå该线ç¨åéæ¶æ¯ã
```java
MyThread thread = new MyThread();
thread.start();
// åå线ç¨åéæ¶æ¯
thread.mHandler.sendEmptyMessage(1);
```
å¨ä½¿ç¨å®Looperä¹åï¼éè¦è°ç¨Looperçquit()æ¹æ³æ¥éåºæ¶æ¯å¾ªç¯ã
```java
Looper.myLooper().quit();
```
éè¦æ³¨æçæ¯ï¼Looperæ¯ä¸ä¸ªè½®è¯¢æ¶æ¯éåçæ é循ç¯ï¼å¦æ没ææ¶æ¯éè¦å¤çï¼ä¼ä¸ç´é»å¡å¨loop()æ¹æ³å¤ï¼å æ¤éè¦è°¨æ 使ç¨ï¼é¿å åºç°æ»å¾ªç¯æå åæ³æ¼çé®é¢ã
找到卡顿来源,BlockCanary源码精简分析
通过屏幕渲染机制我们了解到,Android的屏幕渲染是通过vsync实现的。软件层将数据计算好后,放入缓冲区,硬件层从缓冲区读取数据绘制到屏幕上,渲染周期是ms,这让我们看到不断变化的电影接口源码画面。如果计算时间超过ms,就会出现卡顿现象,这通常发生在软件层,而不是硬件层。卡顿发生的原因在于软件层的计算时间需要小于ms,而计算的执行地点则在Handler中,具体来说是在UI的Handler中。Android进程间的交互通过Binder实现,线程间通信通过Handler。
软件层在收到硬件层的vsync信号后,会在Java层向UI的Handler中投递一个消息,进行view数据的计算。这涉及到测量、布局和绘制,通常在`ViewRootImpl`的`performTraversals()`函数中实现。因此,view数据计算在UI的Handler中执行,如果有其他操作在此执行且耗时过长,则可能导致卡顿,图像打印源码我们需要找到并优化这些操作。
要找到卡顿的原因,可以通过在消息处理前后记录时间,计算时间差,将这个差值与预设的卡顿阈值比较。如果大于阈值,表示发生了卡顿,此时可以dump主线程堆栈并显示给开发者。实现这一功能的关键在于在Looper中设置日志打印类。通过`Looper.loop()`函数中的日志打印,我们可以插入自定义的Printer,并在消息执行前后计算时间差。另一种方法是在日志中添加前缀和后缀,根据这些标志判断时间点。
BlockCanary是一个用于检测Android应用卡顿的工具,通过源码分析,我们可以了解到它的实现逻辑。要使用BlockCanary,首先需要定义一个继承`BlockCanaryContext`的类,并重写其中的关键方法。在应用的`onCreate()`方法中调用BlockCanary的安装方法即可。当卡顿发生时,BlockCanary会通知开发者,并在日志中显示卡顿信息。
BlockCanary的核心逻辑包括安装、事件监控、堆栈和CPU信息的采集等。在事件发生时,会创建LooperMonitor,同时启动堆栈采样和CPU采样。当消息将要执行时,开始记录开始时间,执行完毕后停止记录,并计算执行时间。如果时间差超过预设阈值,表示发生了卡顿,并通过回调传递卡顿信息给开发者。
堆栈和CPU信息的获取通过`AbstractSampler`类实现,它通过`post`一个`Runnable`来触发采样过程,循环调用`doSample()`函数。StackSampler和CpuSampler分别负责堆栈和CPU信息的采集,核心逻辑包括获取当前线程的堆栈信息和CPU速率,并将其保存。获取堆栈信息时,通过在`StackSampler`类中查找指定时间范围内的堆栈信息;获取CPU信息时,从`CpuSampler`类中解析`/proc/stat`和`/proc/mpid/stat`文件的CPU数据,并保存。
总结而言,BlockCanary通过在消息处理前后记录时间差,检测卡顿情况,并通过堆栈和CPU信息提供详细的卡顿分析,帮助开发者定位和优化性能问题。
Android组件系列:再谈Handler机制(Native篇)
前文已介绍过Java层Handler机制的设计与实现,本篇将深入探讨Native层的Looper#loop()为何不会卡死主线程的原理。 从Android 2.3版本开始,Google将Handler的阻塞/唤醒机制从Object#wait() / notify()改为了利用Linux epoll来实现,为的是在Native层引入一套消息管理机制,以支持C/C++开发者。 在Native层实现类似Java层的阻塞/唤醒机制,主要面临两种选择:要么继续使用Object#wait() / notify(),通过Java层通知Native层何时唤醒;要么在Native层重新实现一套阻塞/唤醒方案,并通过JNI调用Java层进入阻塞态。最终,Google选择了后者。 虽然将Java层的阻塞/唤醒机制直接移植到Native层并非必要,使用pthread_cond_wait也能实现相同效果,但epoll提供了一种更高效、更灵活的方案,特别是对于监听多个流事件的需求。理解I/O多路复用之epoll
epoll是Linux I/O多路复用实现之一,与select和poll并列。它能够高效地同时监听多个流事件,而无需为每个流创建单独的线程或阻塞CPU资源。 epoll通过将流事件转发到用户空间,让用户程序能实时响应事件。为了实现这一功能,epoll与eventfd配合使用。eventfd提供了一个用于累计计数的特殊文件描述符,只有当有新事件发生时,用户程序才能从eventfd中读取到计数增加。Native Handler机制解析
Native层Handler机制的核心是Looper、MessageQueue和epoll+eventfd的组合。以下是关键步骤:消息队列初始化
消息队列初始化涉及创建Looper对象,该对象持有mEpollFd和mWakeEventFd两个关键对象。mWakeEventFd用于监听消息队列的新消息,而mEpollFd用于管理监听的流事件。消息循环与阻塞
Java和Native层的消息队列创建后,线程将阻塞在Looper#loop()方法中。在Java层,消息队列的循环与阻塞由nativePollOnce()方法实现,最终调用到NativeMessageQueue#pollOnce()方法。这个方法将请求转发给Looper#pollOnce()方法执行。消息发送与唤醒机制
发送消息时,无论是Java还是Native层,最终都会调用到唤醒线程的方法。Java中,通过nativeWake()方法唤醒,而Native层直接通过write()方法向mWakeEventFd写入值来唤醒线程。唤醒后的消息分发处理
线程唤醒后,首先判断唤醒原因,然后根据不同的情况执行相应的逻辑。关键步骤包括检查mWakeEventFd、处理Native层消息、处理自定义fd的事件等。结语
通过深入理解epoll机制及其与Native Handler的集成,我们可以清晰地理解Handler机制的底层实现。理解了这些关键技术点后,开发者能够更深入地掌握并优化Android应用中的消息处理逻辑。