结合源码讲解下Android中的截图流程
在Android中,截图过程主要涉及以下几个关键步骤:
- 捕获屏幕内容
- 生成Bitmap图像
- 将Bitmap传递给用户应用
在系统内部,截图流程涉及Android Framework、SurfaceFlinger和Gralloc等模块的协作。下面详细介绍这几个步骤的实现过程,并结合源码解释。
1. 触发截图请求
通常,截图是通过以下几种方式触发的:
- 用户按下系统快捷键(如电源键 + 音量下键)来触发截图
- 应用程序通过MediaProjection API来发起录屏或截屏
- 系统调用SurfaceControl.screenshot方法实现
常见的截图调用方式是在系统界面中调用TakeScreenshotService
,此时系统服务会调用截图的核心代码。
// SystemUI ScreenshotController.java
public void takeScreenshot() {
// 调用系统服务的截图请求
mScreenshotHelper.takeScreenshot(
WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
true /* statusBarVisible */,
true /* navBarVisible */,
mHandler,
new Consumer() { ... });
}
2. 截图流程核心方法:SurfaceControl.screenshot
在系统内部,截图操作最终会调用SurfaceControl.screenshot
方法,该方法会通过JNI与Native层进行交互,最终调用到SurfaceFlinger服务来生成截图。
// SurfaceControl.java
public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
return nativeScreenshot(sourceCrop, width, height, rotation);
}
在nativeScreenshot
方法中,它会通过JNI调用到SurfaceControl.cpp
的nativeScreenshot
函数:
// SurfaceControl.cpp
static jobject nativeScreenshot(JNIEnv* env, jobject clazz, ...)
{
sp display = SurfaceComposerClient::getInternalDisplayToken();
sp buffer;
SurfaceComposerClient::screenshot(display, buffer, ...);
return android::Bitmap_createFromGraphicBuffer(env, buffer);
}
3. SurfaceFlinger生成图像数据
在SurfaceComposerClient::screenshot
方法中,会通过IPC调用SurfaceFlinger的截图方法。
// SurfaceComposerClient.cpp
status_t SurfaceComposerClient::screenshot(const sp& display,
sp& outBuffer, ...)
{
return ScreenshotClient::capture(display, outBuffer, ...);
}
ScreenshotClient::capture
会使用GraphicBuffer从显示设备捕获屏幕数据。
// ScreenshotClient.cpp
status_t ScreenshotClient::capture(const sp& display,
sp& outBuffer, ...)
{
// 通过Binder IPC调用SurfaceFlinger服务
return composer->captureScreen(display, outBuffer, ...);
}
4. SurfaceFlinger服务生成截图
在SurfaceFlinger服务的captureScreen
方法中,系统会通过OpenGL ES或硬件加速器来渲染和捕获屏幕内容。SurfaceFlinger会将当前显示内容复制到GraphicBuffer中,最终生成一个Bitmap对象。
// SurfaceFlinger.cpp
status_t SurfaceFlinger::captureScreen(const sp& display,
sp& buffer, ...)
{
// 调用OpenGL渲染屏幕内容
renderScreenToBuffer(buffer, ...);
}
SurfaceFlinger
生成截图的流程包含从多个图层合成最终图像并渲染到GraphicBuffer
。我们深入解读其主要实现逻辑和关键源码。
SurfaceFlinger
生成截图的核心方法是captureScreen
。它通过以下几个步骤生成屏幕截图:
(1). 选择合适的显示设备
(2). 准备目标缓冲区(GraphicBuffer)
(3). 合成图层内容到缓冲区
(4). 将缓冲区数据保存为图像
Step 1: 选择显示设备
SurfaceFlinger
服务通常有多个显示设备,例如内置屏幕、外接显示器等。它首先获取用户指定的显示设备或默认的主显示设备。
// SurfaceFlinger.cpp
status_t SurfaceFlinger::captureScreen(const sp& display,
sp& buffer, ...)
{
// 获取指定的显示设备,或使用默认的内置显示
sp hw = getDisplayDeviceLocked(display);
if (hw == nullptr) {
return NAME_NOT_FOUND;
}
...
}
getDisplayDeviceLocked
会根据IBinder
标识符找到对应的显示设备。每个显示设备都包含了显示的配置信息、分辨率、旋转角度等。
Step 2: 准备目标缓冲区(GraphicBuffer)
在确定了显示设备后,SurfaceFlinger
会创建一个GraphicBuffer
,用于保存截图内容。GraphicBuffer
是一个共享内存区域,可以在不同的进程间传递。
// SurfaceFlinger.cpp
buffer = new GraphicBuffer(reqWidth, reqHeight, PIXEL_FORMAT_RGBA_8888,
GraphicBuffer::USAGE_HW_TEXTURE |
GraphicBuffer::USAGE_HW_RENDER);
在这里,GraphicBuffer
初始化时指定了缓冲区的宽度、高度和格式,PIXEL_FORMAT_RGBA_8888
代表每个像素点包含红、绿、蓝、透明度四个通道,各占8位。
Step 3: 合成图层内容到缓冲区
接下来,SurfaceFlinger
会将所有图层内容绘制到缓冲区中。这是最复杂的部分,主要使用OpenGL ES进行渲染。
SurfaceFlinger
会调用renderScreenToBuffer
方法,该方法会依次将所有显示的图层(例如应用窗口、状态栏等)合成为一个最终图像。
// SurfaceFlinger.cpp
status_t SurfaceFlinger::renderScreenToBuffer(const sp& hw,
const sp& buffer, ...)
{
// 设置OpenGL的绘图目标为新创建的GraphicBuffer
RenderEngine* engine = getRenderEngine();
engine->bindExternalTextureBuffer(buffer->handle, ...);
// 遍历每个图层,依次绘制
for (const auto& layer : hw->getVisibleLayersSortedByZ()) {
// 绘制图层内容
layer->draw(engine);
}
engine->unbindExternalTextureBuffer();
return NO_ERROR;
}
这里的关键在于:
- 使用OpenGL的FBO(帧缓冲区对象)来将绘图目标切换到GraphicBuffer
- 遍历每个图层,并调用layer->draw(engine)将图层内容绘制到缓冲区
图层合成过程
每个图层都包含图像内容和图层属性(如位置、大小、透明度、旋转等)。在draw
方法中,SurfaceFlinger
通过OpenGL的纹理映射和着色器,将每个图层的内容合成到目标缓冲区。
// Layer.cpp
void Layer::draw(RenderEngine& engine) {
// 使用OpenGL纹理和着色器绘制图层内容
engine.drawTexture(mTexture, mPosition, mSize, mAlpha, mMatrix);
}
Step 4: 将缓冲区数据保存为图像
完成合成后,缓冲区中的内容即为整个屏幕的图像。最终,SurfaceFlinger
会将该GraphicBuffer
返回给调用者,并将其转化为一个Bitmap
对象供Java层使用。
// ScreenshotClient.cpp
sp buffer;
SurfaceFlinger::captureScreen(display, buffer, ...);
// 在Java层创建Bitmap
jobject bitmap = android::Bitmap_createFromGraphicBuffer(env, buffer);
综上, SurfaceFlinger
的截图生成过程:
(1). 选择合适的显示设备 获取需要截图的显示设备
(2). 准备目标缓冲区(GraphicBuffer):使用GraphicBuffer为目标图像分配存储空间
(3). 合成图层内容到缓冲区:使用OpenGL渲染所有图层,将内容绘制到缓冲区
(4). 将缓冲区数据保存为图像:将缓冲区内容转换为Bitmap
5. 将Bitmap返回给应用
在Native层生成Bitmap后,会通过JNI将Bitmap传递回Java层,最终在用户应用中可访问该Bitmap。
// SurfaceControl.java
public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
return nativeScreenshot(sourceCrop, width, height, rotation);
}
总结
- 触发截图请求:系统通过快捷键或API触发截图
- 调用截图方法:SurfaceControl.screenshot -> SurfaceFlinger.captureScreen
- 生成屏幕内容:SurfaceFlinger通过OpenGL渲染当前显示内容到GraphicBuffer
- 返回Bitmap:将生成的Bitmap对象传递给应用
原文地址:https://blog.csdn.net/wudexiaoade2008/article/details/143753044
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!